From: Star Zeng Date: Sun, 11 Feb 2018 03:00:44 +0000 (+0800) Subject: SecurityPkg OpalPassword: Add solution without SMM device code X-Git-Tag: edk2-stable201903~2224 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=112e584ba0619695b7da3bb87604b3385ac2cb6d SecurityPkg OpalPassword: Add solution without SMM device code After IOMMU is enabled in S3, original solution with SMM device code (OpalPasswordSmm) to unlock OPAL device for S3 will not work as the DMA operation will be aborted without granted DMA buffer. Instead, this solution is to add OpalPasswordPei to eliminate SMM device code, and OPAL setup UI produced by OpalPasswordDxe will be updated to send requests (set password, update password, and etc), and then the requests will be processed in next boot before SmmReadyToLock, password and device info will be saved to lock box used by OpalPasswordPei to unlock OPAL device for S3. Cc: Jiewen Yao Cc: Eric Dong Cc: Chao Zhang Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Star Zeng Reviewed-by: Jiewen Yao --- diff --git a/SecurityPkg/SecurityPkg.dsc b/SecurityPkg/SecurityPkg.dsc index 65a2fe3d79..f82703a17b 100644 --- a/SecurityPkg/SecurityPkg.dsc +++ b/SecurityPkg/SecurityPkg.dsc @@ -324,6 +324,8 @@ # SecurityPkg/Tcg/Opal/OpalPasswordDxe/OpalPasswordDxe.inf SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf + SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf + SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf [Components.IPF] SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/EsalVariableDxeSal.inf diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/ComponentName.c b/SecurityPkg/Tcg/Opal/OpalPassword/ComponentName.c new file mode 100644 index 0000000000..ef963d0e0b --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/ComponentName.c @@ -0,0 +1,398 @@ +/** @file + UEFI Component Name(2) protocol implementation for Opal driver. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+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 "OpalDriver.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gOpalComponentName = { + OpalEfiDriverComponentNameGetDriverName, + OpalEfiDriverComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gOpalComponentName2 = { + OpalEfiDriverComponentName2GetDriverName, + OpalEfiDriverComponentName2GetControllerName, + "en" +}; + + +/// The name of the driver in all the languages we support. +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mOpalDriverNameTable[] = { + { LANGUAGE_RFC_3066_ENGLISH, (CHAR16*)EFI_DRIVER_NAME_UNICODE }, + { LANGUAGE_ISO_639_2_ENGLISH, (CHAR16*)EFI_DRIVER_NAME_UNICODE }, + { 0, 0 } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentNameGetDriverName( + EFI_COMPONENT_NAME_PROTOCOL* This, + CHAR8* Language, + CHAR16** DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + mOpalDriverNameTable, + DriverName, + TRUE + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentName2GetDriverName( + EFI_COMPONENT_NAME2_PROTOCOL* This, + CHAR8* Language, + CHAR16** DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + mOpalDriverNameTable, + DriverName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +GetControllerName( + EFI_HANDLE ControllerHandle, + EFI_HANDLE ChildHandle, + CHAR8* Language, + CHAR16** ControllerName + ) +{ + if (Language == NULL || ControllerName == NULL || ControllerHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // don't support any controller or children names + return EFI_UNSUPPORTED; +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentNameGetControllerName( + EFI_COMPONENT_NAME_PROTOCOL* This, + EFI_HANDLE ControllerHandle, + EFI_HANDLE ChildHandle, + CHAR8* Language, + CHAR16** ControllerName + ) +{ + return (GetControllerName( ControllerHandle, ChildHandle, Language, ControllerName)); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentName2GetControllerName( + EFI_COMPONENT_NAME2_PROTOCOL* This, + EFI_HANDLE ControllerHandle, + EFI_HANDLE ChildHandle, + CHAR8* Language, + CHAR16** ControllerName + ) +{ + return (GetControllerName(ControllerHandle, ChildHandle, Language, ControllerName)); +} + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.c new file mode 100644 index 0000000000..d51865380f --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.c @@ -0,0 +1,1334 @@ +/** @file + This driver is used for Opal Password Feature support at AHCI mode. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalPasswordPei.h" + +/** + Start command for give slot on specific port. + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param CommandSlot The number of CommandSlot. + @param Timeout The timeout Value of start. + + @retval EFI_DEVICE_ERROR The command start unsuccessfully. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_SUCCESS The command start successfully. + +**/ +EFI_STATUS +EFIAPI +AhciStartCommand ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT8 CommandSlot, + IN UINT64 Timeout + ); + +/** + Stop command running for giving port + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param Timeout The timeout Value of stop. + + @retval EFI_DEVICE_ERROR The command stop unsuccessfully. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_SUCCESS The command stop successfully. + +**/ +EFI_STATUS +EFIAPI +AhciStopCommand ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT64 Timeout + ); + +/** + Read AHCI Operation register. + + @param AhciBar AHCI bar address. + @param Offset The operation register offset. + + @return The register content read. + +**/ +UINT32 +EFIAPI +AhciReadReg ( + IN UINT32 AhciBar, + IN UINT32 Offset + ) +{ + UINT32 Data; + + Data = 0; + + Data = MmioRead32 (AhciBar + Offset); + + return Data; +} + +/** + Write AHCI Operation register. + + @param AhciBar AHCI bar address. + @param Offset The operation register offset. + @param Data The Data used to write down. + +**/ +VOID +EFIAPI +AhciWriteReg ( + IN UINT32 AhciBar, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + MmioWrite32 (AhciBar + Offset, Data); + + return ; +} + +/** + Do AND operation with the Value of AHCI Operation register. + + @param AhciBar AHCI bar address. + @param Offset The operation register offset. + @param AndData The Data used to do AND operation. + +**/ +VOID +EFIAPI +AhciAndReg ( + IN UINT32 AhciBar, + IN UINT32 Offset, + IN UINT32 AndData + ) +{ + UINT32 Data; + + Data = AhciReadReg (AhciBar, Offset); + + Data &= AndData; + + AhciWriteReg (AhciBar, Offset, Data); +} + +/** + Do OR operation with the Value of AHCI Operation register. + + @param AhciBar AHCI bar address. + @param Offset The operation register offset. + @param OrData The Data used to do OR operation. + +**/ +VOID +EFIAPI +AhciOrReg ( + IN UINT32 AhciBar, + IN UINT32 Offset, + IN UINT32 OrData + ) +{ + UINT32 Data; + + Data = AhciReadReg (AhciBar, Offset); + + Data |= OrData; + + AhciWriteReg (AhciBar, Offset, Data); +} + +/** + Wait for memory set to the test Value. + + @param AhciBar AHCI bar address. + @param Offset The memory offset to test. + @param MaskValue The mask Value of memory. + @param TestValue The test Value of memory. + @param Timeout The time out Value for wait memory set. + + @retval EFI_DEVICE_ERROR The memory is not set. + @retval EFI_TIMEOUT The memory setting is time out. + @retval EFI_SUCCESS The memory is correct set. + +**/ +EFI_STATUS +EFIAPI +AhciWaitMmioSet ( + IN UINT32 AhciBar, + IN UINT32 Offset, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT32 Delay; + + Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); + + do { + Value = AhciReadReg (AhciBar, Offset) & MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + + } while (Delay > 0); + + return EFI_TIMEOUT; +} +/** + Wait for the Value of the specified system memory set to the test Value. + + @param Address The system memory address to test. + @param MaskValue The mask Value of memory. + @param TestValue The test Value of memory. + @param Timeout The time out Value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +EFIAPI +AhciWaitMemSet ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT32 Delay; + + Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1); + + do { + // + // Access sytem memory to see if the Value is the tested one. + // + // The system memory pointed by Address will be updated by the + // SATA Host Controller, "volatile" is introduced to prevent + // compiler from optimizing the access to the memory address + // to only read once. + // + Value = *(volatile UINT32 *) (UINTN) Address; + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + + } while (Delay > 0); + + return EFI_TIMEOUT; +} + +/** + Check the memory status to the test Value. + + @param[in] Address The memory address to test. + @param[in] MaskValue The mask Value of memory. + @param[in] TestValue The test Value of memory. + @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once. + + @retval EFI_NOTREADY The memory is not set. + @retval EFI_TIMEOUT The memory setting retry times out. + @retval EFI_SUCCESS The memory is correct set. + +**/ +EFI_STATUS +EFIAPI +AhciCheckMemSet ( + IN UINTN Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN OUT UINTN *RetryTimes OPTIONAL + ) +{ + UINT32 Value; + + if (RetryTimes != NULL) { + (*RetryTimes)--; + } + + Value = *(volatile UINT32 *) Address; + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + if ((RetryTimes != NULL) && (*RetryTimes == 0)) { + return EFI_TIMEOUT; + } else { + return EFI_NOT_READY; + } +} + +/** + Clear the port interrupt and error status. It will also clear + HBA interrupt status. + + @param AhciBar AHCI bar address. + @param Port The number of port. + +**/ +VOID +EFIAPI +AhciClearPortStatus ( + IN UINT32 AhciBar, + IN UINT8 Port + ) +{ + UINT32 Offset; + + // + // Clear any error status + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR; + AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset)); + + // + // Clear any port interrupt status + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS; + AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset)); + + // + // Clear any HBA interrupt status + // + AhciWriteReg (AhciBar, EFI_AHCI_IS_OFFSET, AhciReadReg (AhciBar, EFI_AHCI_IS_OFFSET)); +} + +/** + Enable the FIS running for giving port. + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param Timeout The timeout Value of enabling FIS. + + @retval EFI_DEVICE_ERROR The FIS enable setting fails. + @retval EFI_TIMEOUT The FIS enable setting is time out. + @retval EFI_SUCCESS The FIS enable successfully. + +**/ +EFI_STATUS +EFIAPI +AhciEnableFisReceive ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT64 Timeout + ) +{ + UINT32 Offset; + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_FRE); + + return AhciWaitMmioSet ( + AhciBar, + Offset, + EFI_AHCI_PORT_CMD_FR, + EFI_AHCI_PORT_CMD_FR, + Timeout + ); +} + +/** + Disable the FIS running for giving port. + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param Timeout The timeout Value of disabling FIS. + + @retval EFI_DEVICE_ERROR The FIS disable setting fails. + @retval EFI_TIMEOUT The FIS disable setting is time out. + @retval EFI_UNSUPPORTED The port is in running state. + @retval EFI_SUCCESS The FIS disable successfully. + +**/ +EFI_STATUS +EFIAPI +AhciDisableFisReceive ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT64 Timeout + ) +{ + UINT32 Offset; + UINT32 Data; + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + Data = AhciReadReg (AhciBar, Offset); + + // + // Before disabling Fis receive, the DMA engine of the port should NOT be in running status. + // + if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) { + return EFI_UNSUPPORTED; + } + + // + // Check if the Fis receive DMA engine for the port is running. + // + if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) { + return EFI_SUCCESS; + } + + AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE)); + + return AhciWaitMmioSet ( + AhciBar, + Offset, + EFI_AHCI_PORT_CMD_FR, + 0, + Timeout + ); +} + +/** + Build the command list, command table and prepare the fis receiver. + + @param AhciContext The pointer to the AHCI_CONTEXT. + @param Port The number of port. + @param PortMultiplier The timeout Value of stop. + @param CommandFis The control fis will be used for the transfer. + @param CommandList The command list will be used for the transfer. + @param AtapiCommand The atapi command will be used for the transfer. + @param AtapiCommandLength The Length of the atapi command. + @param CommandSlotNumber The command slot will be used for the transfer. + @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address. + @param DataLength The Data count to be transferred. + +**/ +VOID +EFIAPI +AhciBuildCommand ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN EFI_AHCI_COMMAND_FIS *CommandFis, + IN EFI_AHCI_COMMAND_LIST *CommandList, + IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL, + IN UINT8 AtapiCommandLength, + IN UINT8 CommandSlotNumber, + IN OUT VOID *DataPhysicalAddr, + IN UINT64 DataLength + ) +{ + EFI_AHCI_REGISTERS *AhciRegisters; + UINT32 AhciBar; + UINT64 BaseAddr; + UINT64 PrdtNumber; + UINTN RemainedData; + UINTN MemAddr; + DATA_64 Data64; + UINT32 Offset; + + AhciRegisters = &AhciContext->AhciRegisters; + AhciBar = AhciContext->AhciBar; + + // + // Filling the PRDT + // + PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT); + + // + // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block. + // It also limits that the maximum amount of the PRDT entry in the command table + // is 65535. + // + ASSERT (PrdtNumber <= 1); + + Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis); + + BaseAddr = Data64.Uint64; + + ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS)); + + ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE)); + + CommandFis->AhciCFisPmNum = PortMultiplier; + + CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS)); + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + if (AtapiCommand != NULL) { + CopyMem ( + &AhciRegisters->AhciCommandTable->AtapiCmd, + AtapiCommand, + AtapiCommandLength + ); + + CommandList->AhciCmdA = 1; + CommandList->AhciCmdP = 1; + + AhciOrReg (AhciBar, Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI)); + } else { + AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI)); + } + + RemainedData = (UINTN) DataLength; + MemAddr = (UINTN) DataPhysicalAddr; + CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber; + + AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1; + + Data64.Uint64 = (UINT64)MemAddr; + AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba = Data64.Uint32.Lower32; + AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32; + + // + // Set the last PRDT to Interrupt On Complete + // + AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1; + + CopyMem ( + (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)), + CommandList, + sizeof (EFI_AHCI_COMMAND_LIST) + ); + + Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable; + AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32; + AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32; + AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier; + +} + +/** + Buid a command FIS. + + @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure. + @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure. + +**/ +VOID +EFIAPI +AhciBuildCommandFis ( + IN OUT EFI_AHCI_COMMAND_FIS *CmdFis, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock + ) +{ + ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS)); + + CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D; + // + // Indicator it's a command + // + CmdFis->AhciCFisCmdInd = 0x1; + CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand; + + CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures; + CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp; + + CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber; + CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp; + + CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow; + CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp; + + CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh; + CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp; + + CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount; + CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp; + + CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0); +} + +/** + Start a PIO Data transfer on specific port. + + @param AhciContext The pointer to the AHCI_CONTEXT. + @param Port The number of port. + @param PortMultiplier The timeout Value of stop. + @param AtapiCommand The atapi command will be used for the transfer. + @param AtapiCommandLength The Length of the atapi command. + @param Read The transfer direction. + @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data. + @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data. + @param MemoryAddr The pointer to the Data Buffer. + @param DataCount The Data count to be transferred. + @param Timeout The timeout Value of non Data transfer. + + @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_UNSUPPORTED The device is not ready for transfer. + @retval EFI_SUCCESS The PIO Data transfer executes successfully. + +**/ +EFI_STATUS +EFIAPI +AhciPioTransfer ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL, + IN UINT8 AtapiCommandLength, + IN BOOLEAN Read, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, + IN OUT VOID *MemoryAddr, + IN UINT32 DataCount, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + EFI_AHCI_REGISTERS *AhciRegisters; + UINT32 AhciBar; + UINT32 FisBaseAddr; + UINT32 Offset; + UINT32 Delay; + EFI_AHCI_COMMAND_FIS CFis; + EFI_AHCI_COMMAND_LIST CmdList; + UINT32 PortTfd; + UINT32 PrdCount; + UINT32 OldRfisLo; + UINT32 OldRfisHi; + UINT32 OldCmdListLo; + UINT32 OldCmdListHi; + + AhciRegisters = &AhciContext->AhciRegisters; + AhciBar = AhciContext->AhciBar; + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; + OldRfisLo = AhciReadReg (AhciBar, Offset); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU; + OldRfisHi = AhciReadReg (AhciBar, Offset); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; + AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU; + AhciWriteReg (AhciBar, Offset, 0); + + // + // Single task envrionment, we only use one command table for all port + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; + OldCmdListLo = AhciReadReg (AhciBar, Offset); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU; + OldCmdListHi = AhciReadReg (AhciBar, Offset); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; + AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU; + AhciWriteReg (AhciBar, Offset, 0); + + // + // Package read needed + // + AhciBuildCommandFis (&CFis, AtaCommandBlock); + + ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST)); + + CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4; + CmdList.AhciCmdW = Read ? 0 : 1; + + AhciBuildCommand ( + AhciContext, + Port, + PortMultiplier, + &CFis, + &CmdList, + AtapiCommand, + AtapiCommandLength, + 0, + MemoryAddr, + DataCount + ); + + Status = AhciStartCommand ( + AhciBar, + Port, + 0, + Timeout + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Checking the status and wait the driver sending Data + // + FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis; + if (Read && (AtapiCommand == 0)) { + // + // Wait device sends the PIO setup fis before Data transfer + // + Status = EFI_TIMEOUT; + Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1); + do { + Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET; + + Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0); + if (!EFI_ERROR (Status)) { + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (AhciBar, (UINT32) Offset); + // + // PxTFD will be updated if there is a D2H or SetupFIS received. + // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS. + // + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; + break; + } + + PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc)); + if (PrdCount == DataCount) { + break; + } + } + + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0); + if (!EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + break; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay(100); + + Delay--; + } while (Delay > 0); + } else { + // + // Wait for D2H Fis is received + // + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciWaitMemSet ( + Offset, + EFI_AHCI_FIS_TYPE_MASK, + EFI_AHCI_FIS_REGISTER_D2H, + Timeout + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (AhciBar, (UINT32) Offset); + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; + } + } + +Exit: + AhciStopCommand ( + AhciBar, + Port, + Timeout + ); + + AhciDisableFisReceive ( + AhciBar, + Port, + Timeout + ); + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; + AhciWriteReg (AhciBar, Offset, OldRfisLo); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU; + AhciWriteReg (AhciBar, Offset, OldRfisHi); + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; + AhciWriteReg (AhciBar, Offset, OldCmdListLo); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU; + AhciWriteReg (AhciBar, Offset, OldCmdListHi); + + return Status; +} + +/** + Stop command running for giving port + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param Timeout The timeout Value of stop. + + @retval EFI_DEVICE_ERROR The command stop unsuccessfully. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_SUCCESS The command stop successfully. + +**/ +EFI_STATUS +EFIAPI +AhciStopCommand ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT64 Timeout + ) +{ + UINT32 Offset; + UINT32 Data; + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + Data = AhciReadReg (AhciBar, Offset); + + if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) { + return EFI_SUCCESS; + } + + if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) { + AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST)); + } + + return AhciWaitMmioSet ( + AhciBar, + Offset, + EFI_AHCI_PORT_CMD_CR, + 0, + Timeout + ); +} + +/** + Start command for give slot on specific port. + + @param AhciBar AHCI bar address. + @param Port The number of port. + @param CommandSlot The number of CommandSlot. + @param Timeout The timeout Value of start. + + @retval EFI_DEVICE_ERROR The command start unsuccessfully. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_SUCCESS The command start successfully. + +**/ +EFI_STATUS +EFIAPI +AhciStartCommand ( + IN UINT32 AhciBar, + IN UINT8 Port, + IN UINT8 CommandSlot, + IN UINT64 Timeout + ) +{ + UINT32 CmdSlotBit; + EFI_STATUS Status; + UINT32 PortStatus; + UINT32 StartCmd; + UINT32 PortTfd; + UINT32 Offset; + UINT32 Capability; + + // + // Collect AHCI controller information + // + Capability = AhciReadReg(AhciBar, EFI_AHCI_CAPABILITY_OFFSET); + + CmdSlotBit = (UINT32) (1 << CommandSlot); + + AhciClearPortStatus ( + AhciBar, + Port + ); + + Status = AhciEnableFisReceive ( + AhciBar, + Port, + Timeout + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + PortStatus = AhciReadReg (AhciBar, Offset); + + StartCmd = 0; + if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) { + StartCmd = AhciReadReg (AhciBar, Offset); + StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK; + StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE; + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (AhciBar, Offset); + + if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) { + if ((Capability & BIT24) != 0) { + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_COL); + + AhciWaitMmioSet ( + AhciBar, + Offset, + EFI_AHCI_PORT_CMD_COL, + 0, + Timeout + ); + } + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd); + + // + // Setting the command + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT; + AhciAndReg (AhciBar, Offset, 0); + AhciOrReg (AhciBar, Offset, CmdSlotBit); + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI; + AhciAndReg (AhciBar, Offset, 0); + AhciOrReg (AhciBar, Offset, CmdSlotBit); + return EFI_SUCCESS; +} + + +/** + Do AHCI HBA reset. + + @param[in] AhciBar AHCI bar address. + @param[in] Timeout The timeout Value of reset. + + @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset. + @retval EFI_TIMEOUT The reset operation is time out. + @retval EFI_SUCCESS AHCI controller is reset successfully. + +**/ +EFI_STATUS +EFIAPI +AhciReset ( + IN UINT32 AhciBar, + IN UINT64 Timeout + ) +{ + UINT32 Delay; + UINT32 Value; + UINT32 Capability; + + // + // Collect AHCI controller information + // + Capability = AhciReadReg (AhciBar, EFI_AHCI_CAPABILITY_OFFSET); + + // + // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set + // + if ((Capability & EFI_AHCI_CAP_SAM) == 0) { + AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE); + } + + AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET); + + Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); + + do { + Value = AhciReadReg(AhciBar, EFI_AHCI_GHC_OFFSET); + if ((Value & EFI_AHCI_GHC_RESET) == 0) { + return EFI_SUCCESS; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay(100); + + Delay--; + } while (Delay > 0); + + return EFI_TIMEOUT; + + +} + +/** + Send Buffer cmd to specific device. + + @param[in] AhciContext The pointer to the AHCI_CONTEXT. + @param[in] Port The port number of attached ATA device. + @param[in] PortMultiplier The port number of port multiplier of attached ATA device. + @param[in, out] Buffer The Data Buffer to store IDENTIFY PACKET Data. + + @retval EFI_DEVICE_ERROR The cmd abort with error occurs. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_UNSUPPORTED The device is not ready for executing. + @retval EFI_SUCCESS The cmd executes successfully. + +**/ +EFI_STATUS +EFIAPI +AhciIdentify ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN OUT ATA_IDENTIFY_DATA *Buffer + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + if (AhciContext == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE; + AtaCommandBlock.AtaSectorCount = 1; + + Status = AhciPioTransfer ( + AhciContext, + Port, + PortMultiplier, + NULL, + 0, + TRUE, + &AtaCommandBlock, + NULL, + Buffer, + sizeof (ATA_IDENTIFY_DATA), + ATA_TIMEOUT + ); + + return Status; +} + +/** + Allocate transfer-related data struct which is used at AHCI mode. + + @param[in, out] AhciContext The pointer to the AHCI_CONTEXT. + + @retval EFI_OUT_OF_RESOURCE No enough resource. + @retval EFI_SUCCESS Successful to allocate resource. + +**/ +EFI_STATUS +EFIAPI +AhciAllocateResource ( + IN OUT AHCI_CONTEXT *AhciContext + ) +{ + EFI_STATUS Status; + EFI_AHCI_REGISTERS *AhciRegisters; + EFI_PHYSICAL_ADDRESS DeviceAddress; + VOID *Base; + VOID *Mapping; + + AhciRegisters = &AhciContext->AhciRegisters; + + // + // Allocate resources required by AHCI host controller. + // + Status = IoMmuAllocateBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + &Base, + &DeviceAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base)); + AhciRegisters->AhciRFisMapping = Mapping; + AhciRegisters->AhciRFis = Base; + ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS))); + + Status = IoMmuAllocateBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)), + &Base, + &DeviceAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + AhciRegisters->AhciRFis, + AhciRegisters->AhciRFisMapping + ); + AhciRegisters->AhciRFis = NULL; + return EFI_OUT_OF_RESOURCES; + } + ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base)); + AhciRegisters->AhciCmdListMapping = Mapping; + AhciRegisters->AhciCmdList = Base; + ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST))); + + Status = IoMmuAllocateBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)), + &Base, + &DeviceAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)), + AhciRegisters->AhciCmdList, + AhciRegisters->AhciCmdListMapping + ); + AhciRegisters->AhciCmdList = NULL; + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + AhciRegisters->AhciRFis, + AhciRegisters->AhciRFisMapping + ); + AhciRegisters->AhciRFis = NULL; + return EFI_OUT_OF_RESOURCES; + } + ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base)); + AhciRegisters->AhciCommandTableMapping = Mapping; + AhciRegisters->AhciCommandTable = Base; + ZeroMem (AhciRegisters->AhciCommandTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE))); + + // + // Allocate resources for data transfer. + // + Status = IoMmuAllocateBuffer ( + EFI_SIZE_TO_PAGES (HDD_PAYLOAD), + &Base, + &DeviceAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + AhciRegisters->AhciCommandTable, + AhciRegisters->AhciCommandTableMapping + ); + AhciRegisters->AhciCommandTable = NULL; + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)), + AhciRegisters->AhciCmdList, + AhciRegisters->AhciCmdListMapping + ); + AhciRegisters->AhciCmdList = NULL; + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + AhciRegisters->AhciRFis, + AhciRegisters->AhciRFisMapping + ); + AhciRegisters->AhciRFis = NULL; + return EFI_OUT_OF_RESOURCES; + } + ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base)); + AhciContext->BufferMapping = Mapping; + AhciContext->Buffer = Base; + ZeroMem (AhciContext->Buffer, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (HDD_PAYLOAD)); + + DEBUG (( + DEBUG_INFO, + "%a() AhciContext 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, + AhciContext->Buffer, + AhciRegisters->AhciRFis, + AhciRegisters->AhciCmdList, + AhciRegisters->AhciCommandTable + )); + return EFI_SUCCESS; +} + +/** + Free allocated transfer-related data struct which is used at AHCI mode. + + @param[in, out] AhciContext The pointer to the AHCI_CONTEXT. + +**/ +VOID +EFIAPI +AhciFreeResource ( + IN OUT AHCI_CONTEXT *AhciContext + ) +{ + EFI_AHCI_REGISTERS *AhciRegisters; + + AhciRegisters = &AhciContext->AhciRegisters; + + if (AhciContext->Buffer != NULL) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (HDD_PAYLOAD), + AhciContext->Buffer, + AhciContext->BufferMapping + ); + AhciContext->Buffer = NULL; + } + + if (AhciRegisters->AhciCommandTable != NULL) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)), + AhciRegisters->AhciCommandTable, + AhciRegisters->AhciCommandTableMapping + ); + AhciRegisters->AhciCommandTable = NULL; + } + + if (AhciRegisters->AhciCmdList != NULL) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)), + AhciRegisters->AhciCmdList, + AhciRegisters->AhciCmdListMapping + ); + AhciRegisters->AhciCmdList = NULL; + } + + if (AhciRegisters->AhciRFis != NULL) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)), + AhciRegisters->AhciRFis, + AhciRegisters->AhciRFisMapping + ); + AhciRegisters->AhciRFis = NULL; + } +} + +/** + Initialize ATA host controller at AHCI mode. + + The function is designed to initialize ATA host controller. + + @param[in] AhciContext The pointer to the AHCI_CONTEXT. + @param[in] Port The port number to do initialization. + +**/ +EFI_STATUS +EFIAPI +AhciModeInitialize ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port + ) +{ + EFI_STATUS Status; + EFI_AHCI_REGISTERS *AhciRegisters; + UINT32 AhciBar; + UINT32 Capability; + UINT32 Offset; + UINT32 Data; + UINT32 PhyDetectDelay; + + AhciRegisters = &AhciContext->AhciRegisters; + AhciBar = AhciContext->AhciBar; + + Status = AhciReset (AhciBar, ATA_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Collect AHCI controller information + // + Capability = AhciReadReg (AhciBar, EFI_AHCI_CAPABILITY_OFFSET); + + // + // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set + // + if ((Capability & EFI_AHCI_CAP_SAM) == 0) { + AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE); + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; + AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis); + + // + // Single task envrionment, we only use one command table for all port + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; + AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList); + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + Data = AhciReadReg (AhciBar, Offset); + if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) { + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_POD); + } + + if ((Capability & BIT27) != 0) { + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_SUD); + } + + // + // Disable aggressive power management. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL; + AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT); + // + // Disable the reporting of the corresponding interrupt to system software. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE; + AhciAndReg (AhciBar, Offset, 0); + + Status = AhciEnableFisReceive ( + AhciBar, + Port, + 5000000 + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ + // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec. + // + PhyDetectDelay = 16 * 1000; + do { + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR; + if (AhciReadReg(AhciBar, Offset) != 0) { + AhciWriteReg (AhciBar, Offset, AhciReadReg(AhciBar, Offset)); + } + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + + Data = AhciReadReg (AhciBar, Offset) & EFI_AHCI_PORT_TFD_MASK; + if (Data == 0) { + break; + } + + MicroSecondDelay (1000); + PhyDetectDelay--; + } while (PhyDetectDelay > 0); + + if (PhyDetectDelay == 0) { + return EFI_NOT_FOUND; + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG; + Status = AhciWaitMmioSet ( + AhciBar, + Offset, + 0x0000FFFF, + 0x00000101, + 160000000 + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.h new file mode 100644 index 0000000000..b1d6ed13d5 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.h @@ -0,0 +1,435 @@ +/** @file + Header file for AHCI mode of ATA host controller. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + + +#ifndef __OPAL_PASSWORD_AHCI_MODE_H__ +#define __OPAL_PASSWORD_AHCI_MODE_H__ + +// +// OPAL LIBRARY CALLBACKS +// +#define ATA_COMMAND_TRUSTED_RECEIVE 0x5C +#define ATA_COMMAND_TRUSTED_SEND 0x5E + +// +// ATA TRUSTED commands express transfer Length in 512 byte multiple +// +#define ATA_TRUSTED_TRANSFER_LENGTH_MULTIPLE 512 +#define ATA_DEVICE_LBA 0x40 ///< Set for commands with LBA (rather than CHS) addresses + + +#define EFI_AHCI_BAR_INDEX 0x05 + +#define EFI_AHCI_CAPABILITY_OFFSET 0x0000 +#define EFI_AHCI_CAP_SAM BIT18 +#define EFI_AHCI_GHC_OFFSET 0x0004 +#define EFI_AHCI_GHC_RESET BIT0 +#define EFI_AHCI_GHC_IE BIT1 +#define EFI_AHCI_GHC_ENABLE BIT31 +#define EFI_AHCI_IS_OFFSET 0x0008 +#define EFI_AHCI_PI_OFFSET 0x000C + +typedef struct { + UINT32 Lower32; + UINT32 Upper32; +} DATA_32; + +typedef union { + DATA_32 Uint32; + UINT64 Uint64; +} DATA_64; + +// +// Each PRDT entry can point to a memory block up to 4M byte +// +#define EFI_AHCI_MAX_DATA_PER_PRDT 0x400000 + +#define EFI_AHCI_FIS_REGISTER_H2D 0x27 //Register FIS - Host to Device +#define EFI_AHCI_FIS_REGISTER_H2D_LENGTH 20 +#define EFI_AHCI_FIS_REGISTER_D2H 0x34 //Register FIS - Device to Host +#define EFI_AHCI_FIS_REGISTER_D2H_LENGTH 20 +#define EFI_AHCI_FIS_DMA_ACTIVATE 0x39 //DMA Activate FIS - Device to Host +#define EFI_AHCI_FIS_DMA_ACTIVATE_LENGTH 4 +#define EFI_AHCI_FIS_DMA_SETUP 0x41 //DMA Setup FIS - Bi-directional +#define EFI_AHCI_FIS_DMA_SETUP_LENGTH 28 +#define EFI_AHCI_FIS_DATA 0x46 //Data FIS - Bi-directional +#define EFI_AHCI_FIS_BIST 0x58 //BIST Activate FIS - Bi-directional +#define EFI_AHCI_FIS_BIST_LENGTH 12 +#define EFI_AHCI_FIS_PIO_SETUP 0x5F //PIO Setup FIS - Device to Host +#define EFI_AHCI_FIS_PIO_SETUP_LENGTH 20 +#define EFI_AHCI_FIS_SET_DEVICE 0xA1 //Set Device Bits FIS - Device to Host +#define EFI_AHCI_FIS_SET_DEVICE_LENGTH 8 + +#define EFI_AHCI_D2H_FIS_OFFSET 0x40 +#define EFI_AHCI_DMA_FIS_OFFSET 0x00 +#define EFI_AHCI_PIO_FIS_OFFSET 0x20 +#define EFI_AHCI_SDB_FIS_OFFSET 0x58 +#define EFI_AHCI_FIS_TYPE_MASK 0xFF +#define EFI_AHCI_U_FIS_OFFSET 0x60 + +// +// Port register +// +#define EFI_AHCI_PORT_START 0x0100 +#define EFI_AHCI_PORT_REG_WIDTH 0x0080 +#define EFI_AHCI_PORT_CLB 0x0000 +#define EFI_AHCI_PORT_CLBU 0x0004 +#define EFI_AHCI_PORT_FB 0x0008 +#define EFI_AHCI_PORT_FBU 0x000C +#define EFI_AHCI_PORT_IS 0x0010 +#define EFI_AHCI_PORT_IS_DHRS BIT0 +#define EFI_AHCI_PORT_IS_PSS BIT1 +#define EFI_AHCI_PORT_IS_SSS BIT2 +#define EFI_AHCI_PORT_IS_SDBS BIT3 +#define EFI_AHCI_PORT_IS_UFS BIT4 +#define EFI_AHCI_PORT_IS_DPS BIT5 +#define EFI_AHCI_PORT_IS_PCS BIT6 +#define EFI_AHCI_PORT_IS_DIS BIT7 +#define EFI_AHCI_PORT_IS_PRCS BIT22 +#define EFI_AHCI_PORT_IS_IPMS BIT23 +#define EFI_AHCI_PORT_IS_OFS BIT24 +#define EFI_AHCI_PORT_IS_INFS BIT26 +#define EFI_AHCI_PORT_IS_IFS BIT27 +#define EFI_AHCI_PORT_IS_HBDS BIT28 +#define EFI_AHCI_PORT_IS_HBFS BIT29 +#define EFI_AHCI_PORT_IS_TFES BIT30 +#define EFI_AHCI_PORT_IS_CPDS BIT31 +#define EFI_AHCI_PORT_IS_CLEAR 0xFFFFFFFF +#define EFI_AHCI_PORT_IS_FIS_CLEAR 0x0000000F + +#define EFI_AHCI_PORT_IE 0x0014 +#define EFI_AHCI_PORT_CMD 0x0018 +#define EFI_AHCI_PORT_CMD_ST_MASK 0xFFFFFFFE +#define EFI_AHCI_PORT_CMD_ST BIT0 +#define EFI_AHCI_PORT_CMD_SUD BIT1 +#define EFI_AHCI_PORT_CMD_POD BIT2 +#define EFI_AHCI_PORT_CMD_COL BIT3 +#define EFI_AHCI_PORT_CMD_CR BIT15 +#define EFI_AHCI_PORT_CMD_FRE BIT4 +#define EFI_AHCI_PORT_CMD_FR BIT14 +#define EFI_AHCI_PORT_CMD_MASK ~(EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_FRE | EFI_AHCI_PORT_CMD_COL) +#define EFI_AHCI_PORT_CMD_PMA BIT17 +#define EFI_AHCI_PORT_CMD_HPCP BIT18 +#define EFI_AHCI_PORT_CMD_MPSP BIT19 +#define EFI_AHCI_PORT_CMD_CPD BIT20 +#define EFI_AHCI_PORT_CMD_ESP BIT21 +#define EFI_AHCI_PORT_CMD_ATAPI BIT24 +#define EFI_AHCI_PORT_CMD_DLAE BIT25 +#define EFI_AHCI_PORT_CMD_ALPE BIT26 +#define EFI_AHCI_PORT_CMD_ASP BIT27 +#define EFI_AHCI_PORT_CMD_ICC_MASK (BIT28 | BIT29 | BIT30 | BIT31) +#define EFI_AHCI_PORT_CMD_ACTIVE (1 << 28 ) +#define EFI_AHCI_PORT_TFD 0x0020 +#define EFI_AHCI_PORT_TFD_MASK (BIT7 | BIT3 | BIT0) +#define EFI_AHCI_PORT_TFD_BSY BIT7 +#define EFI_AHCI_PORT_TFD_DRQ BIT3 +#define EFI_AHCI_PORT_TFD_ERR BIT0 +#define EFI_AHCI_PORT_TFD_ERR_MASK 0x00FF00 +#define EFI_AHCI_PORT_SIG 0x0024 +#define EFI_AHCI_PORT_SSTS 0x0028 +#define EFI_AHCI_PORT_SSTS_DET_MASK 0x000F +#define EFI_AHCI_PORT_SSTS_DET 0x0001 +#define EFI_AHCI_PORT_SSTS_DET_PCE 0x0003 +#define EFI_AHCI_PORT_SSTS_SPD_MASK 0x00F0 +#define EFI_AHCI_PORT_SCTL 0x002C +#define EFI_AHCI_PORT_SCTL_DET_MASK 0x000F +#define EFI_AHCI_PORT_SCTL_MASK (~EFI_AHCI_PORT_SCTL_DET_MASK) +#define EFI_AHCI_PORT_SCTL_DET_INIT 0x0001 +#define EFI_AHCI_PORT_SCTL_DET_PHYCOMM 0x0003 +#define EFI_AHCI_PORT_SCTL_SPD_MASK 0x00F0 +#define EFI_AHCI_PORT_SCTL_IPM_MASK 0x0F00 +#define EFI_AHCI_PORT_SCTL_IPM_INIT 0x0300 +#define EFI_AHCI_PORT_SCTL_IPM_PSD 0x0100 +#define EFI_AHCI_PORT_SCTL_IPM_SSD 0x0200 +#define EFI_AHCI_PORT_SERR 0x0030 +#define EFI_AHCI_PORT_SERR_RDIE BIT0 +#define EFI_AHCI_PORT_SERR_RCE BIT1 +#define EFI_AHCI_PORT_SERR_TDIE BIT8 +#define EFI_AHCI_PORT_SERR_PCDIE BIT9 +#define EFI_AHCI_PORT_SERR_PE BIT10 +#define EFI_AHCI_PORT_SERR_IE BIT11 +#define EFI_AHCI_PORT_SERR_PRC BIT16 +#define EFI_AHCI_PORT_SERR_PIE BIT17 +#define EFI_AHCI_PORT_SERR_CW BIT18 +#define EFI_AHCI_PORT_SERR_BDE BIT19 +#define EFI_AHCI_PORT_SERR_DE BIT20 +#define EFI_AHCI_PORT_SERR_CRCE BIT21 +#define EFI_AHCI_PORT_SERR_HE BIT22 +#define EFI_AHCI_PORT_SERR_LSE BIT23 +#define EFI_AHCI_PORT_SERR_TSTE BIT24 +#define EFI_AHCI_PORT_SERR_UFT BIT25 +#define EFI_AHCI_PORT_SERR_EX BIT26 +#define EFI_AHCI_PORT_ERR_CLEAR 0xFFFFFFFF +#define EFI_AHCI_PORT_SACT 0x0034 +#define EFI_AHCI_PORT_CI 0x0038 +#define EFI_AHCI_PORT_SNTF 0x003C + + +#pragma pack(1) +// +// Command List structure includes total 32 entries. +// The entry Data structure is listed at the following. +// +typedef struct { + UINT32 AhciCmdCfl:5; //Command FIS Length + UINT32 AhciCmdA:1; //ATAPI + UINT32 AhciCmdW:1; //Write + UINT32 AhciCmdP:1; //Prefetchable + UINT32 AhciCmdR:1; //Reset + UINT32 AhciCmdB:1; //BIST + UINT32 AhciCmdC:1; //Clear Busy upon R_OK + UINT32 AhciCmdRsvd:1; + UINT32 AhciCmdPmp:4; //Port Multiplier Port + UINT32 AhciCmdPrdtl:16; //Physical Region Descriptor Table Length + UINT32 AhciCmdPrdbc; //Physical Region Descriptor Byte Count + UINT32 AhciCmdCtba; //Command Table Descriptor Base Address + UINT32 AhciCmdCtbau; //Command Table Descriptor Base Address Upper 32-BITs + UINT32 AhciCmdRsvd1[4]; +} EFI_AHCI_COMMAND_LIST; + +// +// This is a software constructed FIS. +// For Data transfer operations, this is the H2D Register FIS format as +// specified in the Serial ATA Revision 2.6 specification. +// +typedef struct { + UINT8 AhciCFisType; + UINT8 AhciCFisPmNum:4; + UINT8 AhciCFisRsvd:1; + UINT8 AhciCFisRsvd1:1; + UINT8 AhciCFisRsvd2:1; + UINT8 AhciCFisCmdInd:1; + UINT8 AhciCFisCmd; + UINT8 AhciCFisFeature; + UINT8 AhciCFisSecNum; + UINT8 AhciCFisClyLow; + UINT8 AhciCFisClyHigh; + UINT8 AhciCFisDevHead; + UINT8 AhciCFisSecNumExp; + UINT8 AhciCFisClyLowExp; + UINT8 AhciCFisClyHighExp; + UINT8 AhciCFisFeatureExp; + UINT8 AhciCFisSecCount; + UINT8 AhciCFisSecCountExp; + UINT8 AhciCFisRsvd3; + UINT8 AhciCFisControl; + UINT8 AhciCFisRsvd4[4]; + UINT8 AhciCFisRsvd5[44]; +} EFI_AHCI_COMMAND_FIS; + +// +// ACMD: ATAPI command (12 or 16 bytes) +// +typedef struct { + UINT8 AtapiCmd[0x10]; +} EFI_AHCI_ATAPI_COMMAND; + +// +// Physical Region Descriptor Table includes up to 65535 entries +// The entry Data structure is listed at the following. +// the actual entry number comes from the PRDTL field in the command +// list entry for this command slot. +// +typedef struct { + UINT32 AhciPrdtDba; //Data Base Address + UINT32 AhciPrdtDbau; //Data Base Address Upper 32-BITs + UINT32 AhciPrdtRsvd; + UINT32 AhciPrdtDbc:22; //Data Byte Count + UINT32 AhciPrdtRsvd1:9; + UINT32 AhciPrdtIoc:1; //Interrupt on Completion +} EFI_AHCI_COMMAND_PRDT; + +// +// Command table Data strucute which is pointed to by the entry in the command list +// +typedef struct { + EFI_AHCI_COMMAND_FIS CommandFis; // A software constructed FIS. + EFI_AHCI_ATAPI_COMMAND AtapiCmd; // 12 or 16 bytes ATAPI cmd. + UINT8 Reserved[0x30]; + EFI_AHCI_COMMAND_PRDT PrdtTable; // The scatter/gather list for Data transfer +} EFI_AHCI_COMMAND_TABLE; + +// +// Received FIS structure +// +typedef struct { + UINT8 AhciDmaSetupFis[0x1C]; // Dma Setup Fis: offset 0x00 + UINT8 AhciDmaSetupFisRsvd[0x04]; + UINT8 AhciPioSetupFis[0x14]; // Pio Setup Fis: offset 0x20 + UINT8 AhciPioSetupFisRsvd[0x0C]; + UINT8 AhciD2HRegisterFis[0x14]; // D2H Register Fis: offset 0x40 + UINT8 AhciD2HRegisterFisRsvd[0x04]; + UINT64 AhciSetDeviceBitsFis; // Set Device Bits Fix: offset 0x58 + UINT8 AhciUnknownFis[0x40]; // Unkonwn Fis: offset 0x60 + UINT8 AhciUnknownFisRsvd[0x60]; +} EFI_AHCI_RECEIVED_FIS; + +#pragma pack() + +typedef struct { + EFI_AHCI_RECEIVED_FIS *AhciRFis; + VOID *AhciRFisMapping; + EFI_AHCI_COMMAND_LIST *AhciCmdList; + VOID *AhciCmdListMapping; + EFI_AHCI_COMMAND_TABLE *AhciCommandTable; + VOID *AhciCommandTableMapping; +} EFI_AHCI_REGISTERS; + +typedef struct { + VOID *Buffer; + VOID *BufferMapping; + EFI_AHCI_REGISTERS AhciRegisters; + UINT32 AhciBar; +} AHCI_CONTEXT; + +/** + Send Buffer cmd to specific device. + + @param AhciContext The pointer to the AHCI_CONTEXT. + @param Port The number of port. + @param PortMultiplier The timeout Value of stop. + @param Buffer The Data Buffer to store IDENTIFY PACKET Data. + + @retval EFI_DEVICE_ERROR The cmd abort with error occurs. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_UNSUPPORTED The device is not ready for executing. + @retval EFI_SUCCESS The cmd executes successfully. + +**/ +EFI_STATUS +EFIAPI +AhciIdentify ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN OUT ATA_IDENTIFY_DATA *Buffer + ); + +/** + Allocate transfer-related data struct which is used at AHCI mode. + + @param[in, out] AhciContext The pointer to the AHCI_CONTEXT. + + @retval EFI_OUT_OF_RESOURCE No enough resource. + @retval EFI_SUCCESS Successful to allocate resource. + +**/ +EFI_STATUS +EFIAPI +AhciAllocateResource ( + IN OUT AHCI_CONTEXT *AhciContext + ); + +/** + Free allocated transfer-related data struct which is used at AHCI mode. + + @param[in, out] AhciContext The pointer to the AHCI_CONTEXT. + +**/ +VOID +EFIAPI +AhciFreeResource ( + IN OUT AHCI_CONTEXT *AhciContext + ); + +/** + Initialize ATA host controller at AHCI mode. + + The function is designed to initialize ATA host controller. + + @param[in] AhciContext The pointer to the AHCI_CONTEXT. + @param[in] Port The port number to do initialization. + +**/ +EFI_STATUS +EFIAPI +AhciModeInitialize ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port + ); + +typedef struct _EFI_ATA_COMMAND_BLOCK { + UINT8 Reserved1[2]; + UINT8 AtaCommand; + UINT8 AtaFeatures; + UINT8 AtaSectorNumber; + UINT8 AtaCylinderLow; + UINT8 AtaCylinderHigh; + UINT8 AtaDeviceHead; + UINT8 AtaSectorNumberExp; + UINT8 AtaCylinderLowExp; + UINT8 AtaCylinderHighExp; + UINT8 AtaFeaturesExp; + UINT8 AtaSectorCount; + UINT8 AtaSectorCountExp; + UINT8 Reserved2[6]; +} EFI_ATA_COMMAND_BLOCK; + +typedef struct _EFI_ATA_STATUS_BLOCK { + UINT8 Reserved1[2]; + UINT8 AtaStatus; + UINT8 AtaError; + UINT8 AtaSectorNumber; + UINT8 AtaCylinderLow; + UINT8 AtaCylinderHigh; + UINT8 AtaDeviceHead; + UINT8 AtaSectorNumberExp; + UINT8 AtaCylinderLowExp; + UINT8 AtaCylinderHighExp; + UINT8 Reserved2; + UINT8 AtaSectorCount; + UINT8 AtaSectorCountExp; + UINT8 Reserved3[6]; +} EFI_ATA_STATUS_BLOCK; + +/** + Start a PIO Data transfer on specific port. + + @param AhciContext The pointer to the AHCI_CONTEXT. + @param Port The number of port. + @param PortMultiplier The timeout Value of stop. + @param AtapiCommand The atapi command will be used for the transfer. + @param AtapiCommandLength The Length of the atapi command. + @param Read The transfer direction. + @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data. + @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data. + @param MemoryAddr The pointer to the Data Buffer. + @param DataCount The Data count to be transferred. + @param Timeout The timeout Value of non Data transfer. + + @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs. + @retval EFI_TIMEOUT The operation is time out. + @retval EFI_UNSUPPORTED The device is not ready for transfer. + @retval EFI_SUCCESS The PIO Data transfer executes successfully. + +**/ +EFI_STATUS +EFIAPI +AhciPioTransfer ( + IN AHCI_CONTEXT *AhciContext, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL, + IN UINT8 AtapiCommandLength, + IN BOOLEAN Read, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, + IN OUT VOID *MemoryAddr, + IN UINT32 DataCount, + IN UINT64 Timeout + ); + + +#endif + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.c new file mode 100644 index 0000000000..d2597ce33e --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.c @@ -0,0 +1,3006 @@ +/** @file + Entrypoint of Opal UEFI Driver and contains all the logic to + register for new Opal device instances. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +// This UEFI driver consumes EFI_STORAGE_SECURITY_PROTOCOL instances and installs an +// HII GUI to manage Opal features if the device is Opal capable +// If the Opal device is being managed by the UEFI Driver, it shall provide a popup +// window during boot requesting a user password + +#include "OpalDriver.h" +#include "OpalHii.h" + +EFI_GUID mOpalDeviceAtaGuid = OPAL_DEVICE_ATA_GUID; +EFI_GUID mOpalDeviceNvmeGuid = OPAL_DEVICE_NVME_GUID; + +BOOLEAN mOpalEndOfDxe = FALSE; +OPAL_REQUEST_VARIABLE *mOpalRequestVariable = NULL; +UINTN mOpalRequestVariableSize = 0; +CHAR16 mPopUpString[256]; + +typedef struct { + UINT32 Address; + S3_BOOT_SCRIPT_LIB_WIDTH Width; +} OPAL_HC_PCI_REGISTER_SAVE; + +// +// To unlock the Intel SATA controller at S3 Resume, restored the following registers. +// +const OPAL_HC_PCI_REGISTER_SAVE mSataHcRegisterSaveTemplate[] = { + {0x9, S3BootScriptWidthUint8}, + {0x10, S3BootScriptWidthUint32}, + {0x14, S3BootScriptWidthUint32}, + {0x18, S3BootScriptWidthUint32}, + {0x1C, S3BootScriptWidthUint32}, + {0x20, S3BootScriptWidthUint32}, + {0x24, S3BootScriptWidthUint32}, + {0x3c, S3BootScriptWidthUint8}, + {0x3d, S3BootScriptWidthUint8}, + {0x40, S3BootScriptWidthUint16}, + {0x42, S3BootScriptWidthUint16}, + {0x92, S3BootScriptWidthUint16}, + {0x94, S3BootScriptWidthUint32}, + {0x9C, S3BootScriptWidthUint32}, + {0x4, S3BootScriptWidthUint16}, +}; + +OPAL_DRIVER mOpalDriver; + +// +// Globals +// +EFI_DRIVER_BINDING_PROTOCOL gOpalDriverBinding = { + OpalEfiDriverBindingSupported, + OpalEfiDriverBindingStart, + OpalEfiDriverBindingStop, + 0x1b, + NULL, + NULL +}; + +/** + + The function determines the available actions for the OPAL_DISK provided. + + @param[in] SupportedAttributes The supported attributes 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 if 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; +} + +/** + 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 + +**/ +TCG_RESULT +EFIAPI +OpalSupportEnableOpalFeature ( + IN OPAL_SESSION *Session, + IN VOID *Msid, + IN UINT32 MsidLength, + IN VOID *Password, + IN UINT32 PassLength + ) +{ + 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 + ); + } + + return Ret; +} + +/** + Update password for the Opal disk. + + @param[in, out] OpalDisk The disk to update password. + @param[in] Password The input password. + @param[in] PasswordLength The input password length. + +**/ +VOID +OpalSupportUpdatePassword ( + IN OUT OPAL_DISK *OpalDisk, + IN VOID *Password, + IN UINT32 PasswordLength + ) +{ + CopyMem (OpalDisk->Password, Password, PasswordLength); + OpalDisk->PasswordLength = (UINT8) PasswordLength; +} + +/** + Extract device info from the device path. + + @param[in] DevicePath Device path info for the device. + @param[out] DevInfoLength Device information length needed. + @param[out] DevInfo Device information extracted. + + @return Device type. + +**/ +UINT8 +ExtractDeviceInfoFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT16 *DevInfoLength, + OUT OPAL_DEVICE_COMMON *DevInfo OPTIONAL + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TmpDevPath; + EFI_DEVICE_PATH_PROTOCOL *TmpDevPath2; + PCI_DEVICE_PATH *PciDevPath; + UINT8 DeviceType; + UINT8 BusNum; + OPAL_PCI_DEVICE *PciDevice; + OPAL_DEVICE_ATA *DevInfoAta; + OPAL_DEVICE_NVME *DevInfoNvme; + SATA_DEVICE_PATH *SataDevPath; + NVME_NAMESPACE_DEVICE_PATH *NvmeDevPath; + + ASSERT (DevicePath != NULL); + ASSERT (DevInfoLength != NULL); + + DeviceType = OPAL_DEVICE_TYPE_UNKNOWN; + *DevInfoLength = 0; + + TmpDevPath = DevicePath; + + // + // Get device type. + // + while (!IsDevicePathEnd (TmpDevPath)) { + if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_SATA_DP) { + // + // SATA + // + if (DevInfo != NULL) { + SataDevPath = (SATA_DEVICE_PATH *) TmpDevPath; + DevInfoAta = (OPAL_DEVICE_ATA *) DevInfo; + DevInfoAta->Port = SataDevPath->HBAPortNumber; + DevInfoAta->PortMultiplierPort = SataDevPath->PortMultiplierPortNumber; + } + DeviceType = OPAL_DEVICE_TYPE_ATA; + *DevInfoLength = sizeof (OPAL_DEVICE_ATA); + break; + } else if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_NVME_NAMESPACE_DP) { + // + // NVMe + // + if (DevInfo != NULL) { + NvmeDevPath = (NVME_NAMESPACE_DEVICE_PATH *) TmpDevPath; + DevInfoNvme = (OPAL_DEVICE_NVME *) DevInfo; + DevInfoNvme->NvmeNamespaceId = NvmeDevPath->NamespaceId; + } + DeviceType = OPAL_DEVICE_TYPE_NVME; + *DevInfoLength = sizeof (OPAL_DEVICE_NVME); + break; + } + TmpDevPath = NextDevicePathNode (TmpDevPath); + } + + // + // Get device info. + // + BusNum = 0; + TmpDevPath = DevicePath; + TmpDevPath2 = NextDevicePathNode (DevicePath); + while (!IsDevicePathEnd (TmpDevPath2)) { + if (TmpDevPath->Type == HARDWARE_DEVICE_PATH && TmpDevPath->SubType == HW_PCI_DP) { + PciDevPath = (PCI_DEVICE_PATH *) TmpDevPath; + if ((TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_NVME_NAMESPACE_DP)|| + (TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_SATA_DP)) { + if (DevInfo != NULL) { + PciDevice = &DevInfo->Device; + PciDevice->Segment = 0; + PciDevice->Bus = BusNum; + PciDevice->Device = PciDevPath->Device; + PciDevice->Function = PciDevPath->Function; + } + } else { + if (DevInfo != NULL) { + PciDevice = (OPAL_PCI_DEVICE *) ((UINTN) DevInfo + *DevInfoLength); + PciDevice->Segment = 0; + PciDevice->Bus = BusNum; + PciDevice->Device = PciDevPath->Device; + PciDevice->Function = PciDevPath->Function; + } + *DevInfoLength += sizeof (OPAL_PCI_DEVICE); + if (TmpDevPath2->Type == HARDWARE_DEVICE_PATH && TmpDevPath2->SubType == HW_PCI_DP) { + BusNum = PciRead8 (PCI_LIB_ADDRESS (BusNum, PciDevPath->Device, PciDevPath->Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET)); + } + } + } + + TmpDevPath = NextDevicePathNode (TmpDevPath); + TmpDevPath2 = NextDevicePathNode (TmpDevPath2); + } + + ASSERT (DeviceType != OPAL_DEVICE_TYPE_UNKNOWN); + return DeviceType; +} + +/** + Save boot script for ATA OPAL device. + + @param[in] DevInfo Pointer to ATA Opal device information. + + **/ +VOID +OpalDeviceAtaSaveBootScript ( + IN OPAL_DEVICE_ATA *DevInfo + ) +{ + UINTN Bus; + UINTN Device; + UINTN Function; + UINTN Index; + EFI_STATUS Status; + UINTN Offset; + UINT64 Address; + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT32 Data; + OPAL_HC_PCI_REGISTER_SAVE *HcRegisterSaveListPtr; + UINTN Count; + + Data = 0; + + Bus = DevInfo->Device.Bus; + Device = DevInfo->Device.Device; + Function = DevInfo->Device.Function; + + HcRegisterSaveListPtr = (OPAL_HC_PCI_REGISTER_SAVE *) mSataHcRegisterSaveTemplate; + Count = sizeof (mSataHcRegisterSaveTemplate) / sizeof (OPAL_HC_PCI_REGISTER_SAVE); + + for (Index = 0; Index < Count; Index++) { + Offset = HcRegisterSaveListPtr[Index].Address; + Width = HcRegisterSaveListPtr[Index].Width; + + switch (Width) { + case S3BootScriptWidthUint8: + Data = (UINT32)PciRead8 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + case S3BootScriptWidthUint16: + Data = (UINT32)PciRead16 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + case S3BootScriptWidthUint32: + Data = PciRead32 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + default: + ASSERT (FALSE); + break; + } + + Address = S3_BOOT_SCRIPT_LIB_PCI_ADDRESS (Bus, Device, Function, Offset); + Status = S3BootScriptSavePciCfgWrite (Width, Address, 1, &Data); + ASSERT_EFI_ERROR (Status); + } +} + +/** + Build ATA OPAL device info and save them to LockBox. + + @param[in] BarAddr Bar address allocated. + + **/ +VOID +BuildOpalDeviceInfoAta ( + IN UINT32 BarAddr + ) +{ + EFI_STATUS Status; + UINT8 DeviceType; + OPAL_DEVICE_ATA *DevInfoAta; + OPAL_DEVICE_ATA *TempDevInfoAta; + UINTN DevInfoLengthAta; + UINT16 DevInfoLength; + OPAL_DRIVER_DEVICE *TmpDev; + + // + // Build ATA OPAL device info and save them to LockBox. + // + DevInfoLengthAta = 0; + TmpDev = mOpalDriver.DeviceList; + while (TmpDev != NULL) { + DeviceType = ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + NULL + ); + if (DeviceType == OPAL_DEVICE_TYPE_ATA) { + DevInfoLengthAta += DevInfoLength; + } + + TmpDev = TmpDev->Next; + } + + if (DevInfoLengthAta == 0) { + return; + } + + DevInfoAta = AllocateZeroPool (DevInfoLengthAta); + ASSERT (DevInfoAta != NULL); + + TempDevInfoAta = DevInfoAta; + TmpDev = mOpalDriver.DeviceList; + while (TmpDev != NULL) { + DeviceType = ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + NULL + ); + if (DeviceType == OPAL_DEVICE_TYPE_ATA) { + ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + (OPAL_DEVICE_COMMON *) TempDevInfoAta + ); + TempDevInfoAta->Length = DevInfoLength; + TempDevInfoAta->OpalBaseComId = TmpDev->OpalDisk.OpalBaseComId; + TempDevInfoAta->BarAddr = BarAddr; + CopyMem ( + TempDevInfoAta->Password, + TmpDev->OpalDisk.Password, + TmpDev->OpalDisk.PasswordLength + ); + TempDevInfoAta->PasswordLength = TmpDev->OpalDisk.PasswordLength; + OpalDeviceAtaSaveBootScript (TempDevInfoAta); + TempDevInfoAta = (OPAL_DEVICE_ATA *) ((UINTN) TempDevInfoAta + DevInfoLength); + } + + TmpDev = TmpDev->Next; + } + + Status = SaveLockBox ( + &mOpalDeviceAtaGuid, + DevInfoAta, + DevInfoLengthAta + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes ( + &mOpalDeviceAtaGuid, + LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY + ); + ASSERT_EFI_ERROR (Status); + + ZeroMem (DevInfoAta, DevInfoLengthAta); + FreePool (DevInfoAta); +} + +/** + Build NVMe OPAL device info and save them to LockBox. + + @param[in] BarAddr Bar address allocated. + + **/ +VOID +BuildOpalDeviceInfoNvme ( + IN UINT32 BarAddr + ) +{ + EFI_STATUS Status; + UINT8 DeviceType; + OPAL_DEVICE_NVME *DevInfoNvme; + OPAL_DEVICE_NVME *TempDevInfoNvme; + UINTN DevInfoLengthNvme; + UINT16 DevInfoLength; + OPAL_DRIVER_DEVICE *TmpDev; + + // + // Build NVMe OPAL device info and save them to LockBox. + // + DevInfoLengthNvme = 0; + TmpDev = mOpalDriver.DeviceList; + while (TmpDev != NULL) { + DeviceType = ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + NULL + ); + if (DeviceType == OPAL_DEVICE_TYPE_NVME) { + DevInfoLengthNvme += DevInfoLength; + } + + TmpDev = TmpDev->Next; + } + + if (DevInfoLengthNvme == 0) { + return; + } + + DevInfoNvme = AllocateZeroPool (DevInfoLengthNvme); + ASSERT (DevInfoNvme != NULL); + + TempDevInfoNvme = DevInfoNvme; + TmpDev = mOpalDriver.DeviceList; + while (TmpDev != NULL) { + DeviceType = ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + NULL + ); + if (DeviceType == OPAL_DEVICE_TYPE_NVME) { + ExtractDeviceInfoFromDevicePath ( + TmpDev->OpalDisk.OpalDevicePath, + &DevInfoLength, + (OPAL_DEVICE_COMMON *) TempDevInfoNvme + ); + TempDevInfoNvme->Length = DevInfoLength; + TempDevInfoNvme->OpalBaseComId = TmpDev->OpalDisk.OpalBaseComId; + TempDevInfoNvme->BarAddr = BarAddr; + CopyMem ( + TempDevInfoNvme->Password, + TmpDev->OpalDisk.Password, + TmpDev->OpalDisk.PasswordLength + ); + TempDevInfoNvme->PasswordLength = TmpDev->OpalDisk.PasswordLength; + TempDevInfoNvme = (OPAL_DEVICE_NVME *) ((UINTN) TempDevInfoNvme + DevInfoLength); + } + + TmpDev = TmpDev->Next; + } + + Status = SaveLockBox ( + &mOpalDeviceNvmeGuid, + DevInfoNvme, + DevInfoLengthNvme + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes ( + &mOpalDeviceNvmeGuid, + LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY + ); + ASSERT_EFI_ERROR (Status); + + ZeroMem (DevInfoNvme, DevInfoLengthNvme); + FreePool (DevInfoNvme); +} + +/** + Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OpalEndOfDxeEventNotify ( + EFI_EVENT Event, + VOID *Context + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINT64 Length; + OPAL_DRIVER_DEVICE *TmpDev; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + mOpalEndOfDxe = TRUE; + + if (mOpalRequestVariable != NULL) { + // + // Free the OPAL request variable buffer here + // as the OPAL requests should have been processed. + // + FreePool (mOpalRequestVariable); + mOpalRequestVariable = NULL; + mOpalRequestVariableSize = 0; + } + + // + // Assume 64K size and alignment are enough. + // + Length = 0x10000; + Address = 0xFFFFFFFF; + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateMaxAddressSearchBottomUp, + EfiGcdMemoryTypeMemoryMappedIo, + 16, // 2^16: 64K Alignment + Length, + &Address, + gImageHandle, + NULL + ); + ASSERT_EFI_ERROR (Status); + + BuildOpalDeviceInfoAta ((UINT32) Address); + BuildOpalDeviceInfoNvme ((UINT32) Address); + + // + // Zero passsword. + // + TmpDev = mOpalDriver.DeviceList; + while (TmpDev != NULL) { + ZeroMem (TmpDev->OpalDisk.Password, TmpDev->OpalDisk.PasswordLength); + TmpDev = TmpDev->Next; + } + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); + + gBS->CloseEvent (Event); +} + +/** + Get Psid input from the popup window. + + @param[in] Dev The device which need Psid to process Psid Revert + OPAL request. + @param[in] PopUpString Pop up string. + @param[out] PressEsc Whether user escape function through Press ESC. + + @retval Password string if success. NULL if failed. + +**/ +CHAR8 * +OpalDriverPopUpPsidInput ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *PopUpString, + OUT BOOLEAN *PressEsc + ) +{ + EFI_INPUT_KEY InputKey; + UINTN InputLength; + CHAR16 Mask[PSID_CHARACTER_LENGTH + 1]; + CHAR16 Unicode[PSID_CHARACTER_LENGTH + 1]; + CHAR8 *Ascii; + + ZeroMem(Unicode, sizeof(Unicode)); + ZeroMem(Mask, sizeof(Mask)); + + *PressEsc = FALSE; + + gST->ConOut->ClearScreen(gST->ConOut); + + InputLength = 0; + while (TRUE) { + Mask[InputLength] = L'_'; + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &InputKey, + PopUpString, + L"---------------------", + Mask, + NULL + ); + + // + // Check key. + // + if (InputKey.ScanCode == SCAN_NULL) { + // + // password finished + // + if (InputKey.UnicodeChar == CHAR_CARRIAGE_RETURN) { + // + // Add the null terminator. + // + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + break; + } else if ((InputKey.UnicodeChar == CHAR_NULL) || + (InputKey.UnicodeChar == CHAR_TAB) || + (InputKey.UnicodeChar == CHAR_LINEFEED) + ) { + continue; + } else { + // + // delete last key entered + // + if (InputKey.UnicodeChar == CHAR_BACKSPACE) { + if (InputLength > 0) { + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + InputLength--; + } + } else { + // + // add Next key entry + // + Unicode[InputLength] = InputKey.UnicodeChar; + Mask[InputLength] = InputKey.UnicodeChar; + InputLength++; + if (InputLength == PSID_CHARACTER_LENGTH) { + // + // Add the null terminator. + // + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + break; + } + } + } + } + + // + // exit on ESC + // + if (InputKey.ScanCode == SCAN_ESC) { + *PressEsc = TRUE; + break; + } + } + + gST->ConOut->ClearScreen(gST->ConOut); + + if (InputLength == 0 || InputKey.ScanCode == SCAN_ESC) { + ZeroMem (Unicode, sizeof (Unicode)); + ZeroMem (Mask, sizeof (Mask)); + return NULL; + } + + Ascii = AllocateZeroPool (PSID_CHARACTER_LENGTH + 1); + if (Ascii == NULL) { + ZeroMem (Unicode, sizeof (Unicode)); + ZeroMem (Mask, sizeof (Mask)); + return NULL; + } + + UnicodeStrToAsciiStrS (Unicode, Ascii, PSID_CHARACTER_LENGTH + 1); + ZeroMem (Unicode, sizeof (Unicode)); + ZeroMem (Mask, sizeof (Mask)); + + return Ascii; +} + + +/** + Get password input from the popup window. + + @param[in] Dev The device which need password to unlock or + process OPAL request. + @param[in] PopUpString1 Pop up string 1. + @param[in] PopUpString2 Pop up string 2. + @param[out] PressEsc Whether user escape function through Press ESC. + + @retval Password string if success. NULL if failed. + +**/ +CHAR8 * +OpalDriverPopUpPasswordInput ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *PopUpString1, + IN CHAR16 *PopUpString2, + OUT BOOLEAN *PressEsc + ) +{ + EFI_INPUT_KEY InputKey; + UINTN InputLength; + CHAR16 Mask[OPAL_MAX_PASSWORD_SIZE + 1]; + CHAR16 Unicode[OPAL_MAX_PASSWORD_SIZE + 1]; + CHAR8 *Ascii; + + ZeroMem(Unicode, sizeof(Unicode)); + ZeroMem(Mask, sizeof(Mask)); + + *PressEsc = FALSE; + + gST->ConOut->ClearScreen(gST->ConOut); + + InputLength = 0; + while (TRUE) { + Mask[InputLength] = L'_'; + if (PopUpString2 == NULL) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &InputKey, + PopUpString1, + L"---------------------", + Mask, + NULL + ); + } else { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &InputKey, + PopUpString1, + PopUpString2, + L"---------------------", + Mask, + NULL + ); + } + + // + // Check key. + // + if (InputKey.ScanCode == SCAN_NULL) { + // + // password finished + // + if (InputKey.UnicodeChar == CHAR_CARRIAGE_RETURN) { + // + // Add the null terminator. + // + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + break; + } else if ((InputKey.UnicodeChar == CHAR_NULL) || + (InputKey.UnicodeChar == CHAR_TAB) || + (InputKey.UnicodeChar == CHAR_LINEFEED) + ) { + continue; + } else { + // + // delete last key entered + // + if (InputKey.UnicodeChar == CHAR_BACKSPACE) { + if (InputLength > 0) { + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + InputLength--; + } + } else { + // + // add Next key entry + // + Unicode[InputLength] = InputKey.UnicodeChar; + Mask[InputLength] = L'*'; + InputLength++; + if (InputLength == OPAL_MAX_PASSWORD_SIZE) { + // + // Add the null terminator. + // + Unicode[InputLength] = 0; + Mask[InputLength] = 0; + break; + } + } + } + } + + // + // exit on ESC + // + if (InputKey.ScanCode == SCAN_ESC) { + *PressEsc = TRUE; + break; + } + } + + gST->ConOut->ClearScreen(gST->ConOut); + + if (InputLength == 0 || InputKey.ScanCode == SCAN_ESC) { + ZeroMem (Unicode, sizeof (Unicode)); + return NULL; + } + + Ascii = AllocateZeroPool (OPAL_MAX_PASSWORD_SIZE + 1); + if (Ascii == NULL) { + ZeroMem (Unicode, sizeof (Unicode)); + return NULL; + } + + UnicodeStrToAsciiStrS (Unicode, Ascii, OPAL_MAX_PASSWORD_SIZE + 1); + ZeroMem (Unicode, sizeof (Unicode)); + + return Ascii; +} + +/** + Check if disk is locked, show popup window and ask for password if it is. + + @param[in] Dev The device which need to be unlocked. + @param[in] RequestString Request string. + +**/ +CHAR16 * +OpalGetPopUpString ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINTN StrLength; + + StrLength = StrLen (RequestString) + 1 + MAX (StrLen (Dev->Name16), StrLen (L"Disk")); + ASSERT (StrLength < sizeof (mPopUpString) / sizeof (CHAR16)); + + if (Dev->Name16 == NULL) { + UnicodeSPrint (mPopUpString, StrLength + 1, L"%s Disk", RequestString); + } else { + UnicodeSPrint (mPopUpString, StrLength + 1, L"%s %s", RequestString, Dev->Name16); + } + + return mPopUpString; +} + +/** + Check if disk is locked, show popup window and ask for password if it is. + + @param[in] Dev The device which need to be unlocked. + @param[in] RequestString Request string. + +**/ +VOID +OpalDriverRequestPassword ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + BOOLEAN IsEnabled; + BOOLEAN IsLocked; + CHAR8 *Password; + UINT32 PasswordLen; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + IsEnabled = OpalFeatureEnabled (&Dev->OpalDisk.SupportedAttributes, &Dev->OpalDisk.LockingFeature); + if (IsEnabled) { + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + IsLocked = OpalDeviceLocked (&Dev->OpalDisk.SupportedAttributes, &Dev->OpalDisk.LockingFeature); + + while (Count < MAX_PASSWORD_TRY_COUNT) { + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, NULL, &PressEsc); + if (PressEsc) { + if (IsLocked) { + // + // Current device in the lock status and + // User not input password and press ESC, + // keep device in lock status and continue boot. + // + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + // + // Keep lock and continue boot. + // + return; + } else { + // + // Let user input password again. + // + continue; + } + } else { + // + // Current device in the unlock status and + // User not input password and press ESC, + // Shutdown the device. + // + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to shutdown, Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); + } else { + // + // Let user input password again. + // + continue; + } + } + } + + if (Password == NULL) { + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + if (IsLocked) { + Ret = OpalUtilUpdateGlobalLockingRange(&Session, Password, PasswordLen, FALSE, FALSE); + } else { + Ret = OpalUtilUpdateGlobalLockingRange(&Session, Password, PasswordLen, TRUE, TRUE); + if (Ret == TcgResultSuccess) { + Ret = OpalUtilUpdateGlobalLockingRange(&Session, Password, PasswordLen, FALSE, FALSE); + } + } + + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid password.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit. Must shutdown!", + L"Press ENTER to shutdown", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); + } + } +} + +/** + Process Enable Feature OPAL request. + + @param[in] Dev The device which has Enable Feature OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestEnableFeature ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *Password; + UINT32 PasswordLen; + CHAR8 *PasswordConfirm; + UINT32 PasswordLenConfirm; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please type in your new password", &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (Password == NULL) { + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + PasswordConfirm = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please confirm your new password", &PressEsc); + if (PasswordConfirm == NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + Count ++; + continue; + } + PasswordLenConfirm = (UINT32) AsciiStrLen(PasswordConfirm); + if ((PasswordLen != PasswordLenConfirm) || + (CompareMem (Password, PasswordConfirm, PasswordLen) != 0)) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Passwords are not the same.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Count ++; + continue; + } + + if (PasswordConfirm != NULL) { + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + } + + Ret = OpalSupportEnableOpalFeature (&Session, Dev->OpalDisk.Msid, Dev->OpalDisk.MsidLength, Password, PasswordLen); + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Disable User OPAL request. + + @param[in] Dev The device which has Disable User OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestDisableUser ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *Password; + UINT32 PasswordLen; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + BOOLEAN PasswordFailed; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, NULL, &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (Password == NULL) { + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + Ret = OpalUtilDisableUser(&Session, Password, PasswordLen, &PasswordFailed); + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid password, request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Psid Revert OPAL request. + + @param[in] Dev The device which has Psid Revert OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestPsidRevert ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *Psid; + UINT32 PsidLen; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + while (Count < MAX_PSID_TRY_COUNT) { + Psid = OpalDriverPopUpPsidInput (Dev, PopUpString, &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input Psid again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input Psid again. + // + continue; + } + } + + if (Psid == NULL) { + Count ++; + continue; + } + PsidLen = (UINT32) AsciiStrLen(Psid); + + Ret = OpalUtilPsidRevert(&Session, Psid, PsidLen); + if (Ret == TcgResultSuccess) { + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Psid != NULL) { + ZeroMem (Psid, PsidLen); + FreePool (Psid); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Psid, request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PSID_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal Psid retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Admin Revert OPAL request. + + @param[in] Dev The device which has Revert OPAL request. + @param[in] KeepUserData Whether to keep user data or not. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestRevert ( + IN OPAL_DRIVER_DEVICE *Dev, + IN BOOLEAN KeepUserData, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *Password; + UINT32 PasswordLen; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + BOOLEAN PasswordFailed; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, NULL, &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (Password == NULL) { + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + if ((Dev->OpalDisk.SupportedAttributes.PyriteSsc == 1) && + (Dev->OpalDisk.LockingFeature.MediaEncryption == 0)) { + // + // For pyrite type device which does not support media encryption, + // it does not accept "Keep User Data" parameter. + // So here hardcode a FALSE for this case. + // + Ret = OpalUtilRevert( + &Session, + FALSE, + Password, + PasswordLen, + &PasswordFailed, + Dev->OpalDisk.Msid, + Dev->OpalDisk.MsidLength + ); + } else { + Ret = OpalUtilRevert( + &Session, + KeepUserData, + Password, + PasswordLen, + &PasswordFailed, + Dev->OpalDisk.Msid, + Dev->OpalDisk.MsidLength + ); + } + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid password, request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Secure Erase OPAL request. + + @param[in] Dev The device which has Secure Erase OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestSecureErase ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *Password; + UINT32 PasswordLen; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + BOOLEAN PasswordFailed; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, NULL, &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (Password == NULL) { + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + Ret = OpalUtilSecureErase(&Session, Password, PasswordLen, &PasswordFailed); + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid password, request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Set Admin Pwd OPAL request. + + @param[in] Dev The device which has Set Admin Pwd Feature OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestSetUserPwd ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *OldPassword; + UINT32 OldPasswordLen; + CHAR8 *Password; + UINT32 PasswordLen; + CHAR8 *PasswordConfirm; + UINT32 PasswordLenConfirm; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + OldPassword = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please type in your password", &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (OldPassword == NULL) { + Count ++; + continue; + } + OldPasswordLen = (UINT32) AsciiStrLen(OldPassword); + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + Ret = OpalUtilVerifyPassword (&Session, OldPassword, OldPasswordLen, OPAL_LOCKING_SP_USER1_AUTHORITY); + if (Ret == TcgResultSuccess) { + DEBUG ((DEBUG_INFO, "Verify with USER1 authority : Success\n")); + } else { + Ret = OpalUtilVerifyPassword (&Session, OldPassword, OldPasswordLen, OPAL_LOCKING_SP_ADMIN1_AUTHORITY); + if (Ret == TcgResultSuccess) { + DEBUG ((DEBUG_INFO, "Verify with ADMIN1 authority: Success\n")); + } else { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + DEBUG ((DEBUG_INFO, "Verify: Failure\n")); + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Incorrect password.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Count ++; + continue; + } + } + + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please type in your new password", &PressEsc); + if (Password == NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + PasswordConfirm = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please confirm your new password", &PressEsc); + if (PasswordConfirm == NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + ZeroMem (Password, PasswordLen); + FreePool (Password); + Count ++; + continue; + } + PasswordLenConfirm = (UINT32) AsciiStrLen(PasswordConfirm); + if ((PasswordLen != PasswordLenConfirm) || + (CompareMem (Password, PasswordConfirm, PasswordLen) != 0)) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + ZeroMem (Password, PasswordLen); + FreePool (Password); + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Passwords are not the same.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Count ++; + continue; + } + + if (PasswordConfirm != NULL) { + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + } + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + Ret = OpalUtilSetUserPassword( + &Session, + OldPassword, + OldPasswordLen, + Password, + PasswordLen + ); + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (OldPassword != NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process Set Admin Pwd OPAL request. + + @param[in] Dev The device which has Set Admin Pwd Feature OPAL request. + @param[in] RequestString Request string. + +**/ +VOID +ProcessOpalRequestSetAdminPwd ( + IN OPAL_DRIVER_DEVICE *Dev, + IN CHAR16 *RequestString + ) +{ + UINT8 Count; + CHAR8 *OldPassword; + UINT32 OldPasswordLen; + CHAR8 *Password; + UINT32 PasswordLen; + CHAR8 *PasswordConfirm; + UINT32 PasswordLenConfirm; + OPAL_SESSION Session; + BOOLEAN PressEsc; + EFI_INPUT_KEY Key; + TCG_RESULT Ret; + CHAR16 *PopUpString; + + if (Dev == NULL) { + return; + } + + DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__)); + + PopUpString = OpalGetPopUpString (Dev, RequestString); + + Count = 0; + + while (Count < MAX_PASSWORD_TRY_COUNT) { + OldPassword = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please type in your password", &PressEsc); + if (PressEsc) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Press ENTER to skip the request and continue boot,", + L"Press ESC to input password again", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->ClearScreen(gST->ConOut); + return; + } else { + // + // Let user input password again. + // + continue; + } + } + + if (OldPassword == NULL) { + Count ++; + continue; + } + OldPasswordLen = (UINT32) AsciiStrLen(OldPassword); + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + Ret = OpalUtilVerifyPassword (&Session, OldPassword, OldPasswordLen, OPAL_LOCKING_SP_ADMIN1_AUTHORITY); + if (Ret == TcgResultSuccess) { + DEBUG ((DEBUG_INFO, "Verify: Success\n")); + } else { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + DEBUG ((DEBUG_INFO, "Verify: Failure\n")); + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Incorrect password.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Count ++; + continue; + } + + Password = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please type in your new password", &PressEsc); + if (Password == NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + Count ++; + continue; + } + PasswordLen = (UINT32) AsciiStrLen(Password); + + PasswordConfirm = OpalDriverPopUpPasswordInput (Dev, PopUpString, L"Please confirm your new password", &PressEsc); + if (PasswordConfirm == NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + ZeroMem (Password, PasswordLen); + FreePool (Password); + Count ++; + continue; + } + PasswordLenConfirm = (UINT32) AsciiStrLen(PasswordConfirm); + if ((PasswordLen != PasswordLenConfirm) || + (CompareMem (Password, PasswordConfirm, PasswordLen) != 0)) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + ZeroMem (Password, PasswordLen); + FreePool (Password); + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Passwords are not the same.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Count ++; + continue; + } + + if (PasswordConfirm != NULL) { + ZeroMem (PasswordConfirm, PasswordLenConfirm); + FreePool (PasswordConfirm); + } + + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->OpalDisk.Sscp; + Session.MediaId = Dev->OpalDisk.MediaId; + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + Ret = OpalUtilSetAdminPassword( + &Session, + OldPassword, + OldPasswordLen, + Password, + PasswordLen + ); + if (Ret == TcgResultSuccess) { + OpalSupportUpdatePassword (&Dev->OpalDisk, Password, PasswordLen); + DEBUG ((DEBUG_INFO, "%s Success\n", RequestString)); + } else { + DEBUG ((DEBUG_INFO, "%s Failure\n", RequestString)); + } + + if (OldPassword != NULL) { + ZeroMem (OldPassword, OldPasswordLen); + FreePool (OldPassword); + } + + if (Password != NULL) { + ZeroMem (Password, PasswordLen); + FreePool (Password); + } + + if (Ret == TcgResultSuccess) { + break; + } + + Count++; + + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Request failed.", + L"Press ENTER to retry", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + + if (Count >= MAX_PASSWORD_TRY_COUNT) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Opal password retry count exceeds the limit.", + L"Press ENTER to skip the request and continue boot", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + gST->ConOut->ClearScreen(gST->ConOut); + } +} + +/** + Process OPAL request. + + @param[in] Dev The device which has OPAL request. + +**/ +VOID +ProcessOpalRequest ( + IN OPAL_DRIVER_DEVICE *Dev + ) +{ + EFI_STATUS Status; + OPAL_REQUEST_VARIABLE *TempVariable; + OPAL_REQUEST_VARIABLE *Variable; + UINTN VariableSize; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInVariable; + UINTN DevicePathSizeInVariable; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN DevicePathSize; + BOOLEAN KeepUserData; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + if (mOpalRequestVariable == NULL) { + Status = GetVariable2 ( + OPAL_REQUEST_VARIABLE_NAME, + &gHiiSetupVariableGuid, + (VOID **) &Variable, + &VariableSize + ); + if (EFI_ERROR (Status) || (Variable == NULL)) { + return; + } + mOpalRequestVariable = Variable; + mOpalRequestVariableSize = VariableSize; + + // + // Delete the OPAL request variable. + // + Status = gRT->SetVariable ( + OPAL_REQUEST_VARIABLE_NAME, + (EFI_GUID *) &gHiiSetupVariableGuid, + 0, + 0, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + Variable = mOpalRequestVariable; + VariableSize = mOpalRequestVariableSize; + } + + // + // Process the OPAL requests. + // + TempVariable = Variable; + while ((VariableSize > sizeof (OPAL_REQUEST_VARIABLE)) && + (VariableSize >= TempVariable->Length) && + (TempVariable->Length > sizeof (OPAL_REQUEST_VARIABLE))) { + DevicePathInVariable = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) TempVariable + sizeof (OPAL_REQUEST_VARIABLE)); + DevicePathSizeInVariable = GetDevicePathSize (DevicePathInVariable); + DevicePath = Dev->OpalDisk.OpalDevicePath; + DevicePathSize = GetDevicePathSize (DevicePath); + if ((DevicePathSize == DevicePathSizeInVariable) && + (CompareMem (DevicePath, DevicePathInVariable, DevicePathSize) == 0)) { + // + // Found the node for the OPAL device. + // + if (TempVariable->OpalRequest.SetAdminPwd != 0) { + ProcessOpalRequestSetAdminPwd (Dev, L"Update Admin Pwd:"); + } + if (TempVariable->OpalRequest.SetUserPwd != 0) { + ProcessOpalRequestSetUserPwd (Dev, L"Set User Pwd:"); + } + if (TempVariable->OpalRequest.SecureErase!= 0) { + ProcessOpalRequestSecureErase (Dev, L"Secure Erase:"); + } + if (TempVariable->OpalRequest.Revert != 0) { + KeepUserData = (BOOLEAN) TempVariable->OpalRequest.KeepUserData; + ProcessOpalRequestRevert ( + Dev, + KeepUserData, + KeepUserData ? L"Admin Revert(keep):" : L"Admin Revert:" + ); + } + if (TempVariable->OpalRequest.PsidRevert != 0) { + ProcessOpalRequestPsidRevert (Dev, L"Psid Revert:"); + } + if (TempVariable->OpalRequest.DisableUser != 0) { + ProcessOpalRequestDisableUser (Dev, L"Disable User:"); + } + if (TempVariable->OpalRequest.EnableFeature != 0) { + ProcessOpalRequestEnableFeature (Dev, L"Enable Feature:"); + } + + break; + } + + VariableSize -= TempVariable->Length; + TempVariable = (OPAL_REQUEST_VARIABLE *) ((UINTN) TempVariable + TempVariable->Length); + } + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); +} + +/** + Add new device to the global device list. + + @param Dev New create device. + +**/ +VOID +AddDeviceToTail( + IN OPAL_DRIVER_DEVICE *Dev + ) +{ + OPAL_DRIVER_DEVICE *TmpDev; + + if (mOpalDriver.DeviceList == NULL) { + mOpalDriver.DeviceList = Dev; + } else { + TmpDev = mOpalDriver.DeviceList; + while (TmpDev->Next != NULL) { + TmpDev = TmpDev->Next; + } + + TmpDev->Next = Dev; + } +} + +/** + Remove one device in the global device list. + + @param Dev The device need to be removed. + +**/ +VOID +RemoveDevice ( + IN OPAL_DRIVER_DEVICE *Dev + ) +{ + OPAL_DRIVER_DEVICE *TmpDev; + + if (mOpalDriver.DeviceList == NULL) { + return; + } + + if (mOpalDriver.DeviceList == Dev) { + mOpalDriver.DeviceList = NULL; + return; + } + + TmpDev = mOpalDriver.DeviceList; + while (TmpDev->Next != NULL) { + if (TmpDev->Next == Dev) { + TmpDev->Next = Dev->Next; + break; + } + } +} + +/** + Get current device count. + + @retval return the current created device count. + +**/ +UINT8 +GetDeviceCount ( + VOID + ) +{ + UINT8 Count; + OPAL_DRIVER_DEVICE *TmpDev; + + Count = 0; + TmpDev = mOpalDriver.DeviceList; + + while (TmpDev != NULL) { + Count++; + TmpDev = TmpDev->Next; + } + + return Count; +} + +/** + Get devcie list info. + + @retval return the device list pointer. +**/ +OPAL_DRIVER_DEVICE* +OpalDriverGetDeviceList( + VOID + ) +{ + return mOpalDriver.DeviceList; +} + +/** + ReadyToBoot callback to send BlockSid command. + + @param Event Pointer to this event + @param Context Event handler private Data + +**/ +VOID +EFIAPI +ReadyToBootCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + OPAL_DRIVER_DEVICE *Itr; + TCG_RESULT Result; + OPAL_SESSION Session; + UINT32 PpStorageFlag; + + gBS->CloseEvent (Event); + + PpStorageFlag = Tcg2PhysicalPresenceLibGetManagementFlags (); + if ((PpStorageFlag & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) { + // + // Send BlockSID command to each Opal disk + // + Itr = mOpalDriver.DeviceList; + while (Itr != NULL) { + if (Itr->OpalDisk.SupportedAttributes.BlockSid) { + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Itr->OpalDisk.Sscp; + Session.MediaId = Itr->OpalDisk.MediaId; + Session.OpalBaseComId = Itr->OpalDisk.OpalBaseComId; + + Result = OpalBlockSid (&Session, TRUE); // HardwareReset must always be TRUE + if (Result != TcgResultSuccess) { + DEBUG ((DEBUG_ERROR, "OpalBlockSid fail\n")); + break; + } + } + + Itr = Itr->Next; + } + } +} + +/** + Stop this Controller. + + @param Dev The device need to be stopped. + +**/ +VOID +OpalDriverStopDevice ( + OPAL_DRIVER_DEVICE *Dev + ) +{ + // + // free each name + // + FreePool(Dev->Name16); + + // + // remove OPAL_DRIVER_DEVICE from the list + // it updates the controllerList pointer + // + RemoveDevice(Dev); + + // + // close protocols that were opened + // + gBS->CloseProtocol( + Dev->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + gOpalDriverBinding.DriverBindingHandle, + Dev->Handle + ); + + gBS->CloseProtocol( + Dev->Handle, + &gEfiBlockIoProtocolGuid, + gOpalDriverBinding.DriverBindingHandle, + Dev->Handle + ); + + FreePool(Dev); +} + +/** + Get devcie name through the component name protocol. + + @param[in] AllHandlesBuffer The handle buffer for current system. + @param[in] NumAllHandles The number of handles for the handle buffer. + @param[in] Dev The device which need to get name. + @param[in] UseComp1 Whether use component name or name2 protocol. + + @retval TRUE Find the name for this device. + @retval FALSE Not found the name for this device. +**/ +BOOLEAN +OpalDriverGetDeviceNameByProtocol( + EFI_HANDLE *AllHandlesBuffer, + UINTN NumAllHandles, + OPAL_DRIVER_DEVICE *Dev, + BOOLEAN UseComp1 + ) +{ + EFI_HANDLE* ProtocolHandlesBuffer; + UINTN NumProtocolHandles; + EFI_STATUS Status; + EFI_COMPONENT_NAME2_PROTOCOL* Cnp1_2; // efi component name and componentName2 have same layout + EFI_GUID Protocol; + UINTN StrLength; + EFI_DEVICE_PATH_PROTOCOL* TmpDevPath; + UINTN Index1; + UINTN Index2; + EFI_HANDLE TmpHandle; + CHAR16 *DevName; + + if (Dev == NULL || AllHandlesBuffer == NULL || NumAllHandles == 0) { + return FALSE; + } + + Protocol = UseComp1 ? gEfiComponentNameProtocolGuid : gEfiComponentName2ProtocolGuid; + + // + // Find all EFI_HANDLES with protocol + // + Status = gBS->LocateHandleBuffer( + ByProtocol, + &Protocol, + NULL, + &NumProtocolHandles, + &ProtocolHandlesBuffer + ); + if (EFI_ERROR(Status)) { + return FALSE; + } + + + // + // Exit early if no supported devices + // + if (NumProtocolHandles == 0) { + return FALSE; + } + + // + // Get printable name by iterating through all protocols + // using the handle as the child, and iterate through all handles for the controller + // exit loop early once found, if not found, then delete device + // storage security protocol instances already exist, add them to internal list + // + Status = EFI_DEVICE_ERROR; + for (Index1 = 0; Index1 < NumProtocolHandles; Index1++) { + DevName = NULL; + + if (Dev->Name16 != NULL) { + return TRUE; + } + + TmpHandle = ProtocolHandlesBuffer[Index1]; + + Status = gBS->OpenProtocol( + TmpHandle, + &Protocol, + (VOID**)&Cnp1_2, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR(Status) || Cnp1_2 == NULL) { + continue; + } + + // + // Use all handles array as controller handle + // + for (Index2 = 0; Index2 < NumAllHandles; Index2++) { + Status = Cnp1_2->GetControllerName( + Cnp1_2, + AllHandlesBuffer[Index2], + Dev->Handle, + LANGUAGE_ISO_639_2_ENGLISH, + &DevName + ); + if (EFI_ERROR(Status)) { + Status = Cnp1_2->GetControllerName( + Cnp1_2, + AllHandlesBuffer[Index2], + Dev->Handle, + LANGUAGE_RFC_3066_ENGLISH, + &DevName + ); + } + if (!EFI_ERROR(Status) && DevName != NULL) { + StrLength = StrLen(DevName) + 1; // Add one for NULL terminator + Dev->Name16 = AllocateZeroPool(StrLength * sizeof (CHAR16)); + ASSERT (Dev->Name16 != NULL); + StrCpyS (Dev->Name16, StrLength, DevName); + Dev->NameZ = (CHAR8*)AllocateZeroPool(StrLength); + UnicodeStrToAsciiStrS (DevName, Dev->NameZ, StrLength); + + // + // Retrieve bridge BDF info and port number or namespace depending on type + // + TmpDevPath = NULL; + Status = gBS->OpenProtocol( + Dev->Handle, + &gEfiDevicePathProtocolGuid, + (VOID**)&TmpDevPath, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR(Status)) { + Dev->OpalDevicePath = DuplicateDevicePath (TmpDevPath); + return TRUE; + } + + if (Dev->Name16 != NULL) { + FreePool(Dev->Name16); + Dev->Name16 = NULL; + } + if (Dev->NameZ != NULL) { + FreePool(Dev->NameZ); + Dev->NameZ = NULL; + } + } + } + } + + return FALSE; +} + +/** + Get devcie name through the component name protocol. + + @param[in] Dev The device which need to get name. + + @retval TRUE Find the name for this device. + @retval FALSE Not found the name for this device. +**/ +BOOLEAN +OpalDriverGetDriverDeviceName( + OPAL_DRIVER_DEVICE *Dev + ) +{ + EFI_HANDLE* AllHandlesBuffer; + UINTN NumAllHandles; + EFI_STATUS Status; + + if (Dev == NULL) { + DEBUG((DEBUG_ERROR | DEBUG_INIT, "OpalDriverGetDriverDeviceName Exiting, Dev=NULL\n")); + return FALSE; + } + + // + // Iterate through ComponentName2 handles to get name, if fails, try ComponentName + // + if (Dev->Name16 == NULL) { + DEBUG((DEBUG_ERROR | DEBUG_INIT, "Name is null, update it\n")); + // + // Find all EFI_HANDLES + // + Status = gBS->LocateHandleBuffer( + AllHandles, + NULL, + NULL, + &NumAllHandles, + &AllHandlesBuffer + ); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "LocateHandleBuffer for AllHandles failed %r\n", Status )); + return FALSE; + } + + // + // Try component Name2 + // + if (!OpalDriverGetDeviceNameByProtocol(AllHandlesBuffer, NumAllHandles, Dev, FALSE)) { + DEBUG((DEBUG_ERROR | DEBUG_INIT, "ComponentName2 failed to get device name, try ComponentName\n")); + if (!OpalDriverGetDeviceNameByProtocol(AllHandlesBuffer, NumAllHandles, Dev, TRUE)) { + DEBUG((DEBUG_ERROR | DEBUG_INIT, "ComponentName failed to get device name, skip device\n")); + return FALSE; + } + } + } + + return TRUE; +} + +/** + Main entry for this driver. + + @param ImageHandle Image Handle this driver. + @param SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS This function always complete successfully. +**/ +EFI_STATUS +EFIAPI +EfiDriverEntryPoint( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE* SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT ReadyToBootEvent; + EFI_EVENT EndOfDxeEvent; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gOpalDriverBinding, + ImageHandle, + &gOpalComponentName, + &gOpalComponentName2 + ); + + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_ERROR, "Install protocols to Opal driver Handle failed\n")); + return Status ; + } + + // + // Initialize Driver object + // + ZeroMem(&mOpalDriver, sizeof(mOpalDriver)); + mOpalDriver.Handle = ImageHandle; + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + OpalEndOfDxeEventNotify, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // register a ReadyToBoot event callback for sending BlockSid command + // + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + ReadyToBootCallback, + (VOID *) &ImageHandle, + &ReadyToBootEvent + ); + + // + // Install Hii packages. + // + HiiInstall(); + + return Status; +} + +/** + Tests to see if this driver supports a given controller. + + This function checks to see if the controller contains an instance of the + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL and the EFI_BLOCK_IO_PROTOCL + and returns EFI_SUCCESS if it does. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The Handle of the controller to test. This Handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath This parameter is ignored. + + @retval EFI_SUCCESS The device contains required protocols + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device does not contain requires protocols + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingSupported( + IN EFI_DRIVER_BINDING_PROTOCOL* This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL* SecurityCommand; + EFI_BLOCK_IO_PROTOCOL* BlkIo; + + if (mOpalEndOfDxe) { + return EFI_UNSUPPORTED; + } + + // + // Test EFI_STORAGE_SECURITY_COMMAND_PROTOCOL on controller Handle. + // + Status = gBS->OpenProtocol( + Controller, + &gEfiStorageSecurityCommandProtocolGuid, + ( VOID ** )&SecurityCommand, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Close protocol and reopen in Start call + // + gBS->CloseProtocol( + Controller, + &gEfiStorageSecurityCommandProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Test EFI_BLOCK_IO_PROTOCOL on controller Handle, required by EFI_STORAGE_SECURITY_COMMAND_PROTOCOL + // function APIs + // + Status = gBS->OpenProtocol( + Controller, + &gEfiBlockIoProtocolGuid, + (VOID **)&BlkIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_INFO, "No EFI_BLOCK_IO_PROTOCOL on controller\n")); + return Status; + } + + // + // Close protocol and reopen in Start call + // + gBS->CloseProtocol( + Controller, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + Enables Opal Management on a supported device if available. + + The start function is designed to be called after the Opal UEFI Driver has confirmed the + "controller", which is a child Handle, contains the EF_STORAGE_SECURITY_COMMAND protocols. + This function will complete the other necessary checks, such as verifying the device supports + the correct version of Opal. Upon verification, it will add the device to the + Opal HII list in order to expose Opal managmeent options. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The Handle of the controller to start. This Handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the Handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child Handle is created by this + driver. + + @retval EFI_SUCCESS Opal management was enabled. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingStart( + IN EFI_DRIVER_BINDING_PROTOCOL* This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlkIo; + OPAL_DRIVER_DEVICE *Dev; + OPAL_DRIVER_DEVICE *Itr; + BOOLEAN Result; + + Itr = mOpalDriver.DeviceList; + while (Itr != NULL) { + if (Controller == Itr->Handle) { + return EFI_SUCCESS; + } + Itr = Itr->Next; + } + + // + // Create internal device for tracking. This allows all disks to be tracked + // by same HII form + // + Dev = (OPAL_DRIVER_DEVICE*)AllocateZeroPool(sizeof(OPAL_DRIVER_DEVICE)); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Dev->Handle = Controller; + + // + // Open EFI_STORAGE_SECURITY_COMMAND_PROTOCOL to perform Opal supported checks + // + Status = gBS->OpenProtocol( + Controller, + &gEfiStorageSecurityCommandProtocolGuid, + (VOID **)&Dev->Sscp, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR(Status)) { + FreePool(Dev); + return Status; + } + + // + // Open EFI_BLOCK_IO_PROTOCOL on controller Handle, required by EFI_STORAGE_SECURITY_COMMAND_PROTOCOL + // function APIs + // + Status = gBS->OpenProtocol( + Controller, + &gEfiBlockIoProtocolGuid, + (VOID **)&BlkIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR(Status)) { + // + // Close storage security that was opened + // + gBS->CloseProtocol( + Controller, + &gEfiStorageSecurityCommandProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool(Dev); + return Status; + } + + // + // Save mediaId + // + Dev->MediaId = BlkIo->Media->MediaId; + + gBS->CloseProtocol( + Controller, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Acquire Ascii printable name of child, if not found, then ignore device + // + Result = OpalDriverGetDriverDeviceName (Dev); + if (!Result) { + goto Done; + } + + Status = OpalDiskInitialize (Dev); + if (EFI_ERROR (Status)) { + goto Done; + } + + AddDeviceToTail(Dev); + + // + // Check if device is locked and prompt for password. + // + OpalDriverRequestPassword (Dev, L"Unlock:"); + + // + // Process OPAL request from last boot. + // + ProcessOpalRequest (Dev); + + return EFI_SUCCESS; + +Done: + // + // free device, close protocols and exit + // + gBS->CloseProtocol( + Controller, + &gEfiStorageSecurityCommandProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool(Dev); + + return EFI_DEVICE_ERROR; +} + +/** + Stop this driver on Controller. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver could not be removed from this device. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingStop( + EFI_DRIVER_BINDING_PROTOCOL* This, + EFI_HANDLE Controller, + UINTN NumberOfChildren, + EFI_HANDLE* ChildHandleBuffer + ) +{ + OPAL_DRIVER_DEVICE* Itr; + + Itr = mOpalDriver.DeviceList; + + // + // does Controller match any of the devices we are managing for Opal + // + while (Itr != NULL) { + if (Itr->Handle == Controller) { + OpalDriverStopDevice (Itr); + return EFI_SUCCESS; + } + + Itr = Itr->Next; + } + + return EFI_NOT_FOUND; +} + + +/** + Unloads UEFI Driver. Very useful for debugging and testing. + + @param ImageHandle Image Handle this driver. + + @retval EFI_SUCCESS This function always complete successfully. + @retval EFI_INVALID_PARAMETER The input ImageHandle is not valid. +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + OPAL_DRIVER_DEVICE *Itr; + + Status = EFI_SUCCESS; + + if (ImageHandle != gImageHandle) { + return (EFI_INVALID_PARAMETER); + } + + // + // Uninstall any interface added to each device by us + // + while (mOpalDriver.DeviceList) { + Itr = mOpalDriver.DeviceList; + // + // Remove OPAL_DRIVER_DEVICE from the list + // it updates the controllerList pointer + // + OpalDriverStopDevice(Itr); + } + + // + // Uninstall the HII capability + // + Status = HiiUninstall(); + + return Status; +} + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.h new file mode 100644 index 0000000000..2154523e93 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalDriver.h @@ -0,0 +1,612 @@ +/** @file + Values defined and used by the Opal UEFI Driver. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _OPAL_DRIVER_H_ +#define _OPAL_DRIVER_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "OpalPasswordCommon.h" +#include "OpalHiiFormValues.h" + +#define EFI_DRIVER_NAME_UNICODE L"1.0 UEFI Opal Driver" + +// UEFI 2.1 +#define LANGUAGE_RFC_3066_ENGLISH ((CHAR8*)"en") + +// UEFI/EFI < 2.1 +#define LANGUAGE_ISO_639_2_ENGLISH ((CHAR8*)"eng") + +#define CONCAT_(x, y) x ## y +#define CONCAT(x, y) CONCAT_(x, y) + +#define UNICODE_STR(x) CONCAT( L, x ) + +extern EFI_DRIVER_BINDING_PROTOCOL gOpalDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gOpalComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gOpalComponentName2; + +#define OPAL_MSID_LENGHT 128 + +#define MAX_PASSWORD_TRY_COUNT 5 + +// PSID Length +#define PSID_CHARACTER_LENGTH 0x20 +#define MAX_PSID_TRY_COUNT 5 + +#pragma pack(1) + +// +// Structure that is used to represent the available actions for an OpalDisk. +// The data can then be utilized to expose/hide certain actions available to an end user +// by the consumer of this library. +// +typedef struct { + // + // Indicates if the disk can support PSID Revert action. should verify disk supports PSID authority + // + UINT16 PsidRevert : 1; + + // + // Indicates if the disk can support Revert action + // + UINT16 Revert : 1; + + // + // Indicates if the user must keep data for revert action. It is true if no media encryption is supported. + // + UINT16 RevertKeepDataForced : 1; + + // + // Indicates if the disk can support set Admin password + // + UINT16 AdminPass : 1; + + // + // Indicates if the disk can support set User password. This action requires that a user + // password is first enabled. + // + UINT16 UserPass : 1; + + // + // Indicates if unlock action is available. Requires disk to be currently locked. + // + UINT16 Unlock : 1; + + // + // Indicates if Secure Erase action is available. Action requires admin credentials and media encryption support. + // + UINT16 SecureErase : 1; + + // + // Indicates if Disable User action is available. Action requires admin credentials. + // + UINT16 DisableUser : 1; +} OPAL_DISK_ACTIONS; + +// +// Structure that is used to represent an OPAL_DISK. +// +typedef struct { + UINT32 MsidLength; // Byte length of MSID Pin for device + UINT8 Msid[OPAL_MSID_LENGHT]; // MSID Pin for device + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *Sscp; + UINT32 MediaId; // MediaId is used by Ssc Protocol. + EFI_DEVICE_PATH_PROTOCOL *OpalDevicePath; + UINT16 OpalBaseComId; // Opal SSC 1 base com id. + OPAL_OWNER_SHIP Owner; + OPAL_DISK_SUPPORT_ATTRIBUTE SupportedAttributes; + TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature; // Locking Feature Descriptor retrieved from performing a Level 0 Discovery + UINT8 PasswordLength; + UINT8 Password[OPAL_MAX_PASSWORD_SIZE]; +} OPAL_DISK; + +// +// Device with block IO protocol +// +typedef struct _OPAL_DRIVER_DEVICE OPAL_DRIVER_DEVICE; + +struct _OPAL_DRIVER_DEVICE { + OPAL_DRIVER_DEVICE *Next; ///< Linked list pointer + EFI_HANDLE Handle; ///< Device handle + OPAL_DISK OpalDisk; ///< User context + CHAR16 *Name16; ///< Allocated/freed by UEFI Filter Driver at device creation/removal + CHAR8 *NameZ; ///< Allocated/freed by UEFI Filter Driver at device creation/removal + UINT32 MediaId; ///< Required parameter for EFI_STORAGE_SECURITY_COMMAND_PROTOCOL, from BLOCK_IO_MEDIA + + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *Sscp; /// Device protocols consumed + EFI_DEVICE_PATH_PROTOCOL *OpalDevicePath; +}; + +// +// Opal Driver UEFI Driver Model +// +typedef struct { + EFI_HANDLE Handle; ///< Driver image handle + OPAL_DRIVER_DEVICE *DeviceList; ///< Linked list of controllers owned by this Driver +} OPAL_DRIVER; + +#pragma pack() + +// +// Retrieves a OPAL_DRIVER_DEVICE based on the pointer to its StorageSecurity protocol. +// +#define DRIVER_DEVICE_FROM_OPALDISK(OpalDiskPointer) (OPAL_DRIVER_DEVICE*)(BASE_CR(OpalDiskPointer, OPAL_DRIVER_DEVICE, OpalDisk)) + +/** + Get devcie list info. + + @retval return the device list pointer. +**/ +OPAL_DRIVER_DEVICE* +OpalDriverGetDeviceList( + VOID + ); + +/** + Get devcie name through the component name protocol. + + @param[in] Dev The device which need to get name. + + @retval TRUE Find the name for this device. + @retval FALSE Not found the name for this device. +**/ +BOOLEAN +OpalDriverGetDriverDeviceName( + OPAL_DRIVER_DEVICE *Dev + ); + +/** + Get current device count. + + @retval return the current created device count. + +**/ +UINT8 +GetDeviceCount ( + VOID + ); + +/** + Update password for the Opal disk. + + @param[in, out] OpalDisk The disk to update password. + @param[in] Password The input password. + @param[in] PasswordLength The input password length. + +**/ +VOID +OpalSupportUpdatePassword ( + IN OUT OPAL_DISK *OpalDisk, + IN VOID *Password, + IN UINT32 PasswordLength + ); + +/** + + 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 + ); + +/** + 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 + +**/ +TCG_RESULT +EFIAPI +OpalSupportEnableOpalFeature ( + IN OPAL_SESSION *Session, + IN VOID *Msid, + IN UINT32 MsidLength, + IN VOID *Password, + IN UINT32 PassLength + ); + +/** + Unloads UEFI Driver. Very useful for debugging and testing. + + @param ImageHandle Image handle this driver. + + @retval EFI_SUCCESS This function always complete successfully. + @retval EFI_INVALID_PARAMETER The input ImageHandle is not valid. +**/ +EFI_STATUS +EFIAPI +EfiDriverUnload( + EFI_HANDLE ImageHandle + ); + + +/** + Test to see if this driver supports Controller. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingSupported( + EFI_DRIVER_BINDING_PROTOCOL* This, + EFI_HANDLE Controller, + EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath + ); + +/** + Enables Opal Management on a supported device if available. + + The start function is designed to be called after the Opal UEFI Driver has confirmed the + "controller", which is a child handle, contains the EF_STORAGE_SECURITY_COMMAND protocols. + This function will complete the other necessary checks, such as verifying the device supports + the correct version of Opal. Upon verification, it will add the device to the + Opal HII list in order to expose Opal managmeent options. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS Opal management was enabled. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingStart( + EFI_DRIVER_BINDING_PROTOCOL* This, + EFI_HANDLE Controller, + EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath + ); + +/** + Stop this driver on Controller. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver could not be removed from this device. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverBindingStop( + EFI_DRIVER_BINDING_PROTOCOL* This, + EFI_HANDLE Controller, + UINTN NumberOfChildren, + EFI_HANDLE* ChildHandleBuffer + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentNameGetDriverName( + EFI_COMPONENT_NAME_PROTOCOL* This, + CHAR8* Language, + CHAR16** DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentNameGetControllerName( + EFI_COMPONENT_NAME_PROTOCOL* This, + EFI_HANDLE ControllerHandle, + EFI_HANDLE ChildHandle, + CHAR8* Language, + CHAR16** ControllerName + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentName2GetDriverName( + EFI_COMPONENT_NAME2_PROTOCOL* This, + CHAR8* Language, + CHAR16** DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +OpalEfiDriverComponentName2GetControllerName( + EFI_COMPONENT_NAME2_PROTOCOL* This, + EFI_HANDLE ControllerHandle, + EFI_HANDLE ChildHandle, + CHAR8* Language, + CHAR16** ControllerName + ); + +#endif //_OPAL_DRIVER_H_ diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.c new file mode 100644 index 0000000000..e4972227b6 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.c @@ -0,0 +1,1178 @@ +/** @file + Implementation of the HII for the Opal UEFI Driver. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalHii.h" + +// +// This is the generated IFR binary Data for each formset defined in VFR. +// This Data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 OpalPasswordFormBin[]; + +// +// This is the generated String package Data for all .UNI files. +// This Data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 OpalPasswordDxeStrings[]; + +CHAR16 OpalPasswordStorageName[] = L"OpalHiiConfig"; + +EFI_HII_CONFIG_ACCESS_PROTOCOL gHiiConfigAccessProtocol; + +// +// Handle to the list of HII packages (forms and strings) for this driver +// +EFI_HII_HANDLE gHiiPackageListHandle = NULL; + +// +// Package List GUID containing all form and string packages +// +const EFI_GUID gHiiPackageListGuid = PACKAGE_LIST_GUID; +const EFI_GUID gHiiSetupVariableGuid = SETUP_VARIABLE_GUID; + +// +// Structure that contains state of the HII +// This structure is updated by Hii.cpp and its contents +// is rendered in the HII. +// +OPAL_HII_CONFIGURATION gHiiConfiguration; + +// +// The device path containing the VENDOR_DEVICE_PATH and EFI_DEVICE_PATH_PROTOCOL +// +HII_VENDOR_DEVICE_PATH gHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8)(sizeof(VENDOR_DEVICE_PATH)), + (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8) + } + }, + OPAL_PASSWORD_CONFIG_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8)(END_DEVICE_PATH_LENGTH), + (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +/** + Get saved OPAL request. + + @param[in] OpalDisk The disk needs to get the saved OPAL request. + @param[out] OpalRequest OPAL request got. + +**/ +VOID +GetSavedOpalRequest ( + IN OPAL_DISK *OpalDisk, + OUT OPAL_REQUEST *OpalRequest + ) +{ + EFI_STATUS Status; + OPAL_REQUEST_VARIABLE *TempVariable; + OPAL_REQUEST_VARIABLE *Variable; + UINTN VariableSize; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInVariable; + UINTN DevicePathSizeInVariable; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN DevicePathSize; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + Variable = NULL; + VariableSize = 0; + + Status = GetVariable2 ( + OPAL_REQUEST_VARIABLE_NAME, + &gHiiSetupVariableGuid, + (VOID **) &Variable, + &VariableSize + ); + if (EFI_ERROR (Status) || (Variable == NULL)) { + return; + } + + TempVariable = Variable; + while ((VariableSize > sizeof (OPAL_REQUEST_VARIABLE)) && + (VariableSize >= TempVariable->Length) && + (TempVariable->Length > sizeof (OPAL_REQUEST_VARIABLE))) { + DevicePathInVariable = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) TempVariable + sizeof (OPAL_REQUEST_VARIABLE)); + DevicePathSizeInVariable = GetDevicePathSize (DevicePathInVariable); + DevicePath = OpalDisk->OpalDevicePath; + DevicePathSize = GetDevicePathSize (DevicePath); + if ((DevicePathSize == DevicePathSizeInVariable) && + (CompareMem (DevicePath, DevicePathInVariable, DevicePathSize) == 0)) { + // + // Found the node for the OPAL device. + // Get the OPAL request. + // + CopyMem (OpalRequest, &TempVariable->OpalRequest, sizeof (OPAL_REQUEST)); + DEBUG (( + DEBUG_INFO, + "OpalRequest got: 0x%x\n", + *OpalRequest + )); + break; + } + VariableSize -= TempVariable->Length; + TempVariable = (OPAL_REQUEST_VARIABLE *) ((UINTN) TempVariable + TempVariable->Length); + } + + FreePool (Variable); + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); +} + +/** + Save OPAL request. + + @param[in] OpalDisk The disk has OPAL request to save. + @param[in] OpalRequest OPAL request to save. + +**/ +VOID +SaveOpalRequest ( + IN OPAL_DISK *OpalDisk, + IN OPAL_REQUEST OpalRequest + ) +{ + EFI_STATUS Status; + OPAL_REQUEST_VARIABLE *TempVariable; + UINTN TempVariableSize; + OPAL_REQUEST_VARIABLE *Variable; + UINTN VariableSize; + OPAL_REQUEST_VARIABLE *NewVariable; + UINTN NewVariableSize; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInVariable; + UINTN DevicePathSizeInVariable; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN DevicePathSize; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + DEBUG (( + DEBUG_INFO, + "OpalRequest to save: 0x%x\n", + OpalRequest + )); + + Variable = NULL; + VariableSize = 0; + NewVariable = NULL; + NewVariableSize = 0; + + Status = GetVariable2 ( + OPAL_REQUEST_VARIABLE_NAME, + &gHiiSetupVariableGuid, + (VOID **) &Variable, + &VariableSize + ); + if (!EFI_ERROR (Status) && (Variable != NULL)) { + TempVariable = Variable; + TempVariableSize = VariableSize; + while ((TempVariableSize > sizeof (OPAL_REQUEST_VARIABLE)) && + (TempVariableSize >= TempVariable->Length) && + (TempVariable->Length > sizeof (OPAL_REQUEST_VARIABLE))) { + DevicePathInVariable = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) TempVariable + sizeof (OPAL_REQUEST_VARIABLE)); + DevicePathSizeInVariable = GetDevicePathSize (DevicePathInVariable); + DevicePath = OpalDisk->OpalDevicePath; + DevicePathSize = GetDevicePathSize (DevicePath); + if ((DevicePathSize == DevicePathSizeInVariable) && + (CompareMem (DevicePath, DevicePathInVariable, DevicePathSize) == 0)) { + // + // Found the node for the OPAL device. + // Update the OPAL request. + // + CopyMem (&TempVariable->OpalRequest, &OpalRequest, sizeof (OPAL_REQUEST)); + NewVariable = Variable; + NewVariableSize = VariableSize; + break; + } + TempVariableSize -= TempVariable->Length; + TempVariable = (OPAL_REQUEST_VARIABLE *) ((UINTN) TempVariable + TempVariable->Length); + } + if (NewVariable == NULL) { + // + // The node for the OPAL device is not found. + // Create node for the OPAL device. + // + DevicePath = OpalDisk->OpalDevicePath; + DevicePathSize = GetDevicePathSize (DevicePath); + NewVariableSize = VariableSize + sizeof (OPAL_REQUEST_VARIABLE) + DevicePathSize; + NewVariable = AllocatePool (NewVariableSize); + ASSERT (NewVariable != NULL); + CopyMem (NewVariable, Variable, VariableSize); + TempVariable = (OPAL_REQUEST_VARIABLE *) ((UINTN) NewVariable + VariableSize); + TempVariable->Length = (UINT32) (sizeof (OPAL_REQUEST_VARIABLE) + DevicePathSize); + CopyMem (&TempVariable->OpalRequest, &OpalRequest, sizeof (OPAL_REQUEST)); + DevicePathInVariable = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) TempVariable + sizeof (OPAL_REQUEST_VARIABLE)); + CopyMem (DevicePathInVariable, DevicePath, DevicePathSize); + } + } else { + DevicePath = OpalDisk->OpalDevicePath; + DevicePathSize = GetDevicePathSize (DevicePath); + NewVariableSize = sizeof (OPAL_REQUEST_VARIABLE) + DevicePathSize; + NewVariable = AllocatePool (NewVariableSize); + ASSERT (NewVariable != NULL); + NewVariable->Length = (UINT32) (sizeof (OPAL_REQUEST_VARIABLE) + DevicePathSize); + CopyMem (&NewVariable->OpalRequest, &OpalRequest, sizeof (OPAL_REQUEST)); + DevicePathInVariable = (EFI_DEVICE_PATH_PROTOCOL *) ((UINTN) NewVariable + sizeof (OPAL_REQUEST_VARIABLE)); + CopyMem (DevicePathInVariable, DevicePath, DevicePathSize); + } + Status = gRT->SetVariable ( + OPAL_REQUEST_VARIABLE_NAME, + (EFI_GUID *) &gHiiSetupVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + NewVariableSize, + NewVariable + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OpalRequest variable set failed (%r)\n", Status)); + } + if (NewVariable != Variable) { + FreePool (NewVariable); + } + if (Variable != NULL) { + FreePool (Variable); + } + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); +} + +/** + Sets the current system state of global config variables. + +**/ +VOID +HiiSetCurrentConfiguration( + VOID + ) +{ + UINT32 PpStorageFlag; + EFI_STRING NewString; + + gHiiConfiguration.NumDisks = GetDeviceCount(); + + // + // Update the BlockSID status string. + // + PpStorageFlag = Tcg2PhysicalPresenceLibGetManagementFlags (); + + if ((PpStorageFlag & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_ENABLED), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } else { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_DISABLED), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } + HiiSetString(gHiiPackageListHandle, STRING_TOKEN(STR_BLOCKSID_STATUS1), NewString, NULL); + FreePool (NewString); + + if ((PpStorageFlag & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_PP_REQUIRED_FOR_ENABLE_BLOCK_SID) != 0) { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID_TRUE), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } else { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID_FALSE), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } + HiiSetString(gHiiPackageListHandle, STRING_TOKEN(STR_BLOCKSID_STATUS2), NewString, NULL); + FreePool (NewString); + + if ((PpStorageFlag & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_PP_REQUIRED_FOR_DISABLE_BLOCK_SID) != 0) { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_DISK_INFO_DISABLE_BLOCKSID_TRUE), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } else { + NewString = HiiGetString (gHiiPackageListHandle, STRING_TOKEN(STR_DISK_INFO_DISABLE_BLOCKSID_FALSE), NULL); + if (NewString == NULL) { + DEBUG ((DEBUG_INFO, "HiiSetCurrentConfiguration: HiiGetString( ) failed\n")); + return; + } + } + HiiSetString(gHiiPackageListHandle, STRING_TOKEN(STR_BLOCKSID_STATUS3), NewString, NULL); + FreePool (NewString); +} + +/** + Install the HII related resources. + + @retval EFI_SUCCESS Install all the resources success. + @retval other Error occur when install the resources. +**/ +EFI_STATUS +HiiInstall( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE DriverHandle; + + // + // Clear the global configuration. + // + ZeroMem(&gHiiConfiguration, sizeof(gHiiConfiguration)); + + // + // Obtain the driver handle that the BIOS assigned us + // + DriverHandle = HiiGetDriverImageHandleCB(); + + // + // Populate the config access protocol with the three functions we are publishing + // + gHiiConfigAccessProtocol.ExtractConfig = ExtractConfig; + gHiiConfigAccessProtocol.RouteConfig = RouteConfig; + gHiiConfigAccessProtocol.Callback = DriverCallback; + + // + // Associate the required protocols with our driver handle + // + Status = gBS->InstallMultipleProtocolInterfaces( + &DriverHandle, + &gEfiHiiConfigAccessProtocolGuid, + &gHiiConfigAccessProtocol, // HII callback + &gEfiDevicePathProtocolGuid, + &gHiiVendorDevicePath, // required for HII callback allow all disks to be shown in same hii + NULL + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + return OpalHiiAddPackages(); +} + +/** + Install the HII form and string packages. + + @retval EFI_SUCCESS Install all the resources success. + @retval EFI_OUT_OF_RESOURCES Out of resource error. +**/ +EFI_STATUS +OpalHiiAddPackages( + VOID + ) +{ + EFI_HANDLE DriverHandle; + CHAR16 *NewString; + + DriverHandle = HiiGetDriverImageHandleCB(); + + // + // Publish the HII form and HII string packages + // + gHiiPackageListHandle = HiiAddPackages( + &gHiiPackageListGuid, + DriverHandle, + OpalPasswordDxeStrings, + OpalPasswordFormBin, + (VOID*)NULL + ); + + // + // Make sure the packages installed successfully + // + if (gHiiPackageListHandle == NULL) { + DEBUG ((DEBUG_INFO, "OpalHiiAddPackages failed\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Update Version String in main window + // + NewString = HiiGetDriverNameCB (); + if (HiiSetString(gHiiPackageListHandle, STRING_TOKEN(STR_MAIN_OPAL_VERSION), NewString, NULL) == 0) { + DEBUG ((DEBUG_INFO, "OpalHiiAddPackages: HiiSetString( ) failed\n")); + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Uninstall the HII capability. + + @retval EFI_SUCCESS Uninstall all the resources success. + @retval others Other errors occur when unistall the hii resource. +**/ +EFI_STATUS +HiiUninstall( + VOID + ) +{ + EFI_STATUS Status; + + // + // Remove the packages we've provided to the BIOS + // + HiiRemovePackages(gHiiPackageListHandle); + + // + // Remove the protocols from our driver handle + // + Status = gBS->UninstallMultipleProtocolInterfaces( + HiiGetDriverImageHandleCB(), + &gEfiHiiConfigAccessProtocolGuid, + &gHiiConfigAccessProtocol, // HII callback + &gEfiDevicePathProtocolGuid, + &gHiiVendorDevicePath, // required for HII callback + NULL + ); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "Cannot uninstall Hii Protocols: %r\n", Status)); + } + + return Status; +} + +/** + Updates the main menu form. + + @retval EFI_SUCCESS update the main form success. +**/ +EFI_STATUS +HiiPopulateMainMenuForm ( + VOID + ) +{ + UINT8 Index; + CHAR8 *DiskName; + EFI_STRING_ID DiskNameId; + OPAL_DISK *OpalDisk; + + HiiSetCurrentConfiguration(); + + gHiiConfiguration.SupportedDisks = 0; + + for (Index = 0; Index < gHiiConfiguration.NumDisks; Index++) { + OpalDisk = HiiGetOpalDiskCB (Index); + if ((OpalDisk != NULL) && OpalFeatureSupported (&OpalDisk->SupportedAttributes)) { + gHiiConfiguration.SupportedDisks |= (1 << Index); + DiskNameId = GetDiskNameStringId (Index); + DiskName = HiiDiskGetNameCB (Index); + if ((DiskName == NULL) || (DiskNameId == 0)) { + return EFI_UNSUPPORTED; + } + HiiSetFormString(DiskNameId, DiskName); + } + } + + OpalHiiSetBrowserData (); + return EFI_SUCCESS; +} + +/** + Get disk name string id. + + @param DiskIndex The input disk index info. + + @retval The disk name string id. + +**/ +EFI_STRING_ID +GetDiskNameStringId( + UINT8 DiskIndex + ) +{ + switch (DiskIndex) { + case 0: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_0); + case 1: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_1); + case 2: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_2); + case 3: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_3); + case 4: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_4); + case 5: return STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_5); + } + return 0; +} + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original + exporting driver. + @param ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +DriverCallback( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + EFI_BROWSER_ACTION Action, + EFI_QUESTION_ID QuestionId, + UINT8 Type, + EFI_IFR_TYPE_VALUE *Value, + EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + HII_KEY HiiKey; + UINT8 HiiKeyId; + UINT32 PpRequest; + OPAL_DISK *OpalDisk; + + if (ActionRequest != NULL) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; + } else { + return EFI_INVALID_PARAMETER; + } + + // + // If QuestionId is an auto-generated key (label, empty line, etc.), ignore it. + // + if ((QuestionId & HII_KEY_FLAG) == 0) { + return EFI_SUCCESS; + } + + HiiKey.Raw = QuestionId; + HiiKeyId = (UINT8) HiiKey.KeyBits.Id; + + if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { + switch (HiiKeyId) { + case HII_KEY_ID_VAR_SUPPORTED_DISKS: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_VAR_SUPPORTED_DISKS\n")); + return HiiPopulateMainMenuForm (); + + case HII_KEY_ID_VAR_SELECTED_DISK_AVAILABLE_ACTIONS: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_VAR_SELECTED_DISK_AVAILABLE_ACTIONS\n")); + return HiiPopulateDiskInfoForm(); + } + } else if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (HiiKeyId) { + case HII_KEY_ID_GOTO_DISK_INFO: + return HiiSelectDisk((UINT8)HiiKey.KeyBits.Index); + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (HiiKeyId) { + case HII_KEY_ID_BLOCKSID: + switch (Value->u8) { + case 0: + PpRequest = TCG2_PHYSICAL_PRESENCE_NO_ACTION; + break; + + case 1: + PpRequest = TCG2_PHYSICAL_PRESENCE_ENABLE_BLOCK_SID; + break; + + case 2: + PpRequest = TCG2_PHYSICAL_PRESENCE_DISABLE_BLOCK_SID; + break; + + case 3: + PpRequest = TCG2_PHYSICAL_PRESENCE_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_FUNC_TRUE; + break; + + case 4: + PpRequest = TCG2_PHYSICAL_PRESENCE_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_FUNC_FALSE; + break; + + case 5: + PpRequest = TCG2_PHYSICAL_PRESENCE_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_FUNC_TRUE; + break; + + case 6: + PpRequest = TCG2_PHYSICAL_PRESENCE_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_FUNC_FALSE; + break; + + default: + PpRequest = TCG2_PHYSICAL_PRESENCE_NO_ACTION; + DEBUG ((DEBUG_ERROR, "Invalid value input!\n")); + break; + } + HiiSetBlockSidAction(PpRequest); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_SET_ADMIN_PWD: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_SET_ADMIN_PWD\n")); + gHiiConfiguration.OpalRequest.SetAdminPwd = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_SET_USER_PWD: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_SET_USER_PWD\n")); + gHiiConfiguration.OpalRequest.SetUserPwd = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_SECURE_ERASE: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_SECURE_ERASE\n")); + gHiiConfiguration.OpalRequest.SecureErase = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_REVERT: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_REVERT\n")); + gHiiConfiguration.OpalRequest.Revert = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + case HII_KEY_ID_KEEP_USER_DATA: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_KEEP_USER_DATA\n")); + gHiiConfiguration.OpalRequest.KeepUserData = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_PSID_REVERT: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_PSID_REVERT\n")); + gHiiConfiguration.OpalRequest.PsidRevert = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_DISABLE_USER: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_DISABLE_USER\n")); + gHiiConfiguration.OpalRequest.DisableUser = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + case HII_KEY_ID_ENABLE_FEATURE: + DEBUG ((DEBUG_INFO, "HII_KEY_ID_ENABLE_FEATURE\n")); + gHiiConfiguration.OpalRequest.EnableFeature = Value->b; + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + if (OpalDisk != NULL) { + SaveOpalRequest (OpalDisk, gHiiConfiguration.OpalRequest); + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + return EFI_SUCCESS; + + default: + break; + } + } + + return EFI_UNSUPPORTED; +} + +/** + Update the global Disk index info. + + @param Index The input disk index info. + + @retval EFI_SUCCESS Update the disk index info success. + +**/ +EFI_STATUS +HiiSelectDisk( + UINT8 Index + ) +{ + OpalHiiGetBrowserData(); + gHiiConfiguration.SelectedDiskIndex = Index; + OpalHiiSetBrowserData (); + + return EFI_SUCCESS; +} + +/** + Draws the disk info form. + + @retval EFI_SUCCESS Draw the disk info success. + +**/ +EFI_STATUS +HiiPopulateDiskInfoForm( + VOID + ) +{ + OPAL_DISK* OpalDisk; + OPAL_DISK_ACTIONS AvailActions; + TCG_RESULT Ret; + CHAR8 *DiskName; + + OpalHiiGetBrowserData(); + + DiskName = HiiDiskGetNameCB (gHiiConfiguration.SelectedDiskIndex); + if (DiskName == NULL) { + return EFI_UNSUPPORTED; + } + HiiSetFormString(STRING_TOKEN(STR_DISK_INFO_SELECTED_DISK_NAME), DiskName); + + gHiiConfiguration.SelectedDiskAvailableActions = HII_ACTION_NONE; + ZeroMem (&gHiiConfiguration.OpalRequest, sizeof (OPAL_REQUEST)); + gHiiConfiguration.KeepUserDataForced = FALSE; + + OpalDisk = HiiGetOpalDiskCB(gHiiConfiguration.SelectedDiskIndex); + + if (OpalDisk != NULL) { + OpalDiskUpdateStatus (OpalDisk); + Ret = OpalSupportGetAvailableActions(&OpalDisk->SupportedAttributes, &OpalDisk->LockingFeature, OpalDisk->Owner, &AvailActions); + if (Ret == TcgResultSuccess) { + // + // Update actions, always allow PSID Revert + // + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.PsidRevert == 1) ? HII_ACTION_PSID_REVERT : HII_ACTION_NONE; + + // + // Always allow unlock to handle device migration + // + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.Unlock == 1) ? HII_ACTION_UNLOCK : HII_ACTION_NONE; + + if (!OpalFeatureEnabled (&OpalDisk->SupportedAttributes, &OpalDisk->LockingFeature)) { + if (OpalDisk->Owner == OpalOwnershipNobody) { + gHiiConfiguration.SelectedDiskAvailableActions |= HII_ACTION_ENABLE_FEATURE; + + // + // Update strings + // + HiiSetFormString( STRING_TOKEN(STR_DISK_INFO_PSID_REVERT), "PSID Revert to factory default"); + } else { + DEBUG ((DEBUG_INFO, "Feature disabled but ownership != nobody\n")); + } + } else { + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.Revert == 1) ? HII_ACTION_REVERT : HII_ACTION_NONE; + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.AdminPass == 1) ? HII_ACTION_SET_ADMIN_PWD : HII_ACTION_NONE; + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.UserPass == 1) ? HII_ACTION_SET_USER_PWD : HII_ACTION_NONE; + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.SecureErase == 1) ? HII_ACTION_SECURE_ERASE : HII_ACTION_NONE; + gHiiConfiguration.SelectedDiskAvailableActions |= (AvailActions.DisableUser == 1) ? HII_ACTION_DISABLE_USER : HII_ACTION_NONE; + + HiiSetFormString (STRING_TOKEN(STR_DISK_INFO_PSID_REVERT), "PSID Revert to factory default and Disable"); + + // + // Determine revert options for disk + // Default initialize keep user Data to be true + // + gHiiConfiguration.OpalRequest.KeepUserData = 1; + if (AvailActions.RevertKeepDataForced) { + gHiiConfiguration.KeepUserDataForced = TRUE; + } + } + } + + GetSavedOpalRequest (OpalDisk, &gHiiConfiguration.OpalRequest); + } + + // + // Pass the current configuration to the BIOS + // + OpalHiiSetBrowserData (); + + return EFI_SUCCESS; +} + +/** + Send BlockSid request through TPM physical presence module. + + @param PpRequest TPM physical presence operation request. + + @retval EFI_SUCCESS Do the required action success. + @retval Others Other error occur. + +**/ +EFI_STATUS +HiiSetBlockSidAction ( + IN UINT32 PpRequest + ) +{ + UINT32 ReturnCode; + EFI_STATUS Status; + + ReturnCode = Tcg2PhysicalPresenceLibSubmitRequestToPreOSFunction (PpRequest, 0); + if (ReturnCode == TCG_PP_SUBMIT_REQUEST_TO_PREOS_SUCCESS) { + Status = EFI_SUCCESS; + } else if (ReturnCode == TCG_PP_SUBMIT_REQUEST_TO_PREOS_GENERAL_FAILURE) { + Status = EFI_OUT_OF_RESOURCES; + } else if (ReturnCode == TCG_PP_SUBMIT_REQUEST_TO_PREOS_NOT_IMPLEMENTED) { + Status = EFI_UNSUPPORTED; + } else { + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in + format. + @param Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +RouteConfig( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + CONST EFI_STRING Configuration, + EFI_STRING *Progress + ) +{ + if (Configuration == NULL || Progress == NULL) { + return (EFI_INVALID_PARAMETER); + } + + *Progress = Configuration; + if (!HiiIsConfigHdrMatch (Configuration, &gHiiSetupVariableGuid, OpalPasswordStorageName)) { + return EFI_NOT_FOUND; + } + + *Progress = Configuration + StrLen (Configuration); + + return EFI_SUCCESS; +} + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in + format. + @param Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +ExtractConfig( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + CONST EFI_STRING Request, + EFI_STRING *Progress, + EFI_STRING *Results + ) +{ + EFI_STATUS Status; + EFI_STRING ConfigRequest; + EFI_STRING ConfigRequestHdr; + UINTN BufferSize; + UINTN Size; + BOOLEAN AllocatedRequest; + EFI_HANDLE DriverHandle; + + // + // Check for valid parameters + // + if (Progress == NULL || Results == NULL) { + return (EFI_INVALID_PARAMETER); + } + + *Progress = Request; + if ((Request != NULL) && + !HiiIsConfigHdrMatch (Request, &gHiiSetupVariableGuid, OpalPasswordStorageName)) { + return EFI_NOT_FOUND; + } + + AllocatedRequest = FALSE; + BufferSize = sizeof (OPAL_HII_CONFIGURATION); + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + DriverHandle = HiiGetDriverImageHandleCB(); + ConfigRequestHdr = HiiConstructConfigHdr (&gHiiSetupVariableGuid, OpalPasswordStorageName, DriverHandle); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + if (ConfigRequest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + } + + // + // Convert Buffer Data to by helper function BlockToConfig( ) + // + Status = gHiiConfigRouting->BlockToConfig( + gHiiConfigRouting, + ConfigRequest, + (UINT8*)&gHiiConfiguration, + sizeof(OPAL_HII_CONFIGURATION), + Results, + Progress + ); + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return (Status); +} + + +/** + + Pass the current system state to the bios via the hii_G_Configuration. + +**/ +VOID +OpalHiiSetBrowserData ( + VOID + ) +{ + HiiSetBrowserData( + &gHiiSetupVariableGuid, + (CHAR16*)L"OpalHiiConfig", + sizeof(gHiiConfiguration), + (UINT8*)&gHiiConfiguration, + NULL + ); +} + + +/** + + Populate the hii_g_Configuraton with the browser Data. + +**/ +VOID +OpalHiiGetBrowserData ( + VOID + ) +{ + HiiGetBrowserData( + &gHiiSetupVariableGuid, + (CHAR16*)L"OpalHiiConfig", + sizeof(gHiiConfiguration), + (UINT8*)&gHiiConfiguration + ); +} + +/** + Set a string Value in a form. + + @param DestStringId The stringid which need to update. + @param SrcAsciiStr The string nned to update. + + @retval EFI_SUCCESS Do the required action success. + @retval Others Other error occur. + +**/ +EFI_STATUS +HiiSetFormString( + EFI_STRING_ID DestStringId, + CHAR8 *SrcAsciiStr + ) +{ + UINT32 Len; + UINT32 UniSize; + CHAR16* UniStr; + + // + // Determine the Length of the sting + // + Len = ( UINT32 )AsciiStrLen( SrcAsciiStr ); + + // + // Allocate space for the unicode string, including terminator + // + UniSize = (Len + 1) * sizeof(CHAR16); + UniStr = (CHAR16*)AllocateZeroPool(UniSize); + + // + // Copy into unicode string, then copy into string id + // + AsciiStrToUnicodeStrS ( SrcAsciiStr, UniStr, Len + 1); + + // + // Update the string in the form + // + if (HiiSetString(gHiiPackageListHandle, DestStringId, UniStr, NULL) == 0) { + DEBUG ((DEBUG_INFO, "HiiSetFormString( ) failed\n")); + FreePool(UniStr); + return (EFI_OUT_OF_RESOURCES); + } + + // + // Free the memory + // + FreePool(UniStr); + + return (EFI_SUCCESS); +} + +/** + Initialize the Opal disk base on the hardware info get from device. + + @param Dev The Opal device. + + @retval EFI_SUCESS Initialize the device success. + @retval EFI_DEVICE_ERROR Get info from device failed. + +**/ +EFI_STATUS +OpalDiskInitialize ( + IN OPAL_DRIVER_DEVICE *Dev + ) +{ + TCG_RESULT TcgResult; + OPAL_SESSION Session; + + ZeroMem(&Dev->OpalDisk, sizeof(OPAL_DISK)); + Dev->OpalDisk.Sscp = Dev->Sscp; + Dev->OpalDisk.MediaId = Dev->MediaId; + Dev->OpalDisk.OpalDevicePath = Dev->OpalDevicePath; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = Dev->Sscp; + Session.MediaId = Dev->MediaId; + + TcgResult = OpalGetSupportedAttributesInfo (&Session, &Dev->OpalDisk.SupportedAttributes, &Dev->OpalDisk.OpalBaseComId); + if (TcgResult != TcgResultSuccess) { + return EFI_DEVICE_ERROR; + } + Session.OpalBaseComId = Dev->OpalDisk.OpalBaseComId; + + TcgResult = OpalUtilGetMsid (&Session, Dev->OpalDisk.Msid, OPAL_MSID_LENGHT, &Dev->OpalDisk.MsidLength); + if (TcgResult != TcgResultSuccess) { + return EFI_DEVICE_ERROR; + } + + return OpalDiskUpdateStatus (&Dev->OpalDisk); +} + +/** + Update the device info. + + @param OpalDisk The Opal device. + + @retval EFI_SUCESS Initialize the device success. + @retval EFI_DEVICE_ERROR Get info from device failed. + @retval EFI_INVALID_PARAMETER Not get Msid info before get ownership info. + +**/ +EFI_STATUS +OpalDiskUpdateStatus ( + OPAL_DISK *OpalDisk + ) +{ + TCG_RESULT TcgResult; + OPAL_SESSION Session; + + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = OpalDisk->Sscp; + Session.MediaId = OpalDisk->MediaId; + Session.OpalBaseComId = OpalDisk->OpalBaseComId; + + TcgResult = OpalGetLockingInfo(&Session, &OpalDisk->LockingFeature); + if (TcgResult != TcgResultSuccess) { + return EFI_DEVICE_ERROR; + } + + if (OpalDisk->MsidLength == 0) { + return EFI_INVALID_PARAMETER; + } else { + // + // Base on the Msid info to get the ownership, so Msid info must get first. + // + OpalDisk->Owner = OpalUtilDetermineOwnership(&Session, OpalDisk->Msid, OpalDisk->MsidLength); + } + + return EFI_SUCCESS; +} + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.h new file mode 100644 index 0000000000..a1b1131c13 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHii.h @@ -0,0 +1,380 @@ +/** @file + Public Header file of HII library used by Opal UEFI Driver. + Defines required callbacks of Opal HII library. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _OPAL_HII_H_ +#define _OPAL_HII_H_ + +#include + +#include "OpalDriver.h" +#include "OpalHiiFormValues.h" + +#define OPAL_PASSWORD_CONFIG_GUID \ + { \ + 0x0d510a4f, 0xa81b, 0x473f, { 0x87, 0x07, 0xb7, 0xfd, 0xfb, 0xc0, 0x45, 0xba } \ + } + +#pragma pack(1) + +typedef struct { + UINT16 Id: HII_KEY_ID_BITS; + UINT16 Index: HII_KEY_INDEX_BITS; + UINT16 Flag: HII_KEY_FLAG_BITS; +} KEY_BITS; + +typedef union { + UINT16 Raw; + KEY_BITS KeyBits; +} HII_KEY; + +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +#pragma pack() + +extern const EFI_GUID gHiiSetupVariableGuid; + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in + format. + @param Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +RouteConfig( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + CONST EFI_STRING Configuration, + EFI_STRING *Progress + ); + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in + format. + @param Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +ExtractConfig( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + CONST EFI_STRING Request, + EFI_STRING *Progress, + EFI_STRING *Results + ); + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original + exporting driver. + @param ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +DriverCallback( + CONST EFI_HII_CONFIG_ACCESS_PROTOCOL* This, + EFI_BROWSER_ACTION Action, + EFI_QUESTION_ID QuestionId, + UINT8 Type, + EFI_IFR_TYPE_VALUE* Value, + EFI_BROWSER_ACTION_REQUEST* ActionRequest + ); + +/** + + Pass the current system state to the bios via the hii_G_Configuration. + +**/ +VOID +OpalHiiSetBrowserData ( + VOID + ); + +/** + + Populate the hii_g_Configuraton with the browser Data. + +**/ +VOID +OpalHiiGetBrowserData ( + VOID + ); + +/** + Draws the disk info form. + + @retval EFI_SUCCESS Draw the disk info success. + +**/ +EFI_STATUS +HiiPopulateDiskInfoForm( + VOID + ); + +/** + Update the global Disk index info. + + @param Index The input disk index info. + + @retval EFI_SUCCESS Update the disk index info success. + +**/ +EFI_STATUS +HiiSelectDisk( + UINT8 Index + ); + +/** + Use the input password to do the specified action. + + @param Str The input password saved in. + + @retval EFI_SUCCESS Do the required action success. + @retval Others Other error occur. + +**/ +EFI_STATUS +HiiPasswordEntered( + EFI_STRING_ID Str + ); + +/** + Update block sid info. + + @param PpRequest Input the Pp Request. + + @retval EFI_SUCCESS Do the required action success. + @retval Others Other error occur. + +**/ +EFI_STATUS +HiiSetBlockSidAction ( + UINT32 PpRequest + ); + +/** + Reverts the Opal disk to factory default. + + @param PsidStringId The string id for the PSID info. + + @retval EFI_SUCCESS Do the required action success. + +**/ +EFI_STATUS +HiiPsidRevert( + EFI_STRING_ID PsidStringId + ); + +/** + Get disk name string id. + + @param DiskIndex The input disk index info. + + @retval The disk name string id. + +**/ +EFI_STRING_ID +GetDiskNameStringId( + UINT8 DiskIndex + ); + +/** + Update the device info. + + @param OpalDisk The Opal device. + + @retval EFI_SUCESS Initialize the device success. + @retval EFI_DEVICE_ERROR Get info from device failed. + @retval EFI_INVALID_PARAMETER Not get Msid info before get ownership info. + +**/ +EFI_STATUS +OpalDiskUpdateStatus ( + OPAL_DISK *OpalDisk + ); + +/** + Get the driver image handle. + + @retval the driver image handle. + +**/ +EFI_HANDLE +HiiGetDriverImageHandleCB( + VOID + ); + +/** + Install the HII form and string packages. + + @retval EFI_SUCCESS Install all the resources success. + @retval EFI_OUT_OF_RESOURCES Out of resource error. +**/ +EFI_STATUS +OpalHiiAddPackages( + VOID + ); + +/** + Check whether enable feature or not. + + @retval Return the disk number. + +**/ +UINT8 +HiiGetNumConfigRequiredOpalDisksCB( + VOID + ); + +/** + Returns the driver name. + + @retval Returns the driver name. + +**/ +CHAR16* +HiiGetDriverNameCB( + VOID + ); + +/** + Returns the opaque pointer to a physical disk context. + + @param DiskIndex Input the disk index. + + @retval The device pointer. + +**/ +OPAL_DISK* +HiiGetOpalDiskCB( + UINT8 DiskIndex + ); + +/** + Returns the disk name. + + @param DiskIndex Input the disk index. + + @retval Returns the disk name. + +**/ +CHAR8* +HiiDiskGetNameCB( + UINT8 DiskIndex + ); + +/** + Set a string Value in a form. + + @param DestStringId The stringid which need to update. + @param SrcAsciiStr The string nned to update. + + @retval EFI_SUCCESS Do the required action success. + @retval Others Other error occur. + +**/ +EFI_STATUS +HiiSetFormString( + EFI_STRING_ID DestStringId, + CHAR8 *SrcAsciiStr + ); + +/** + Install the HII related resources. + + @retval EFI_SUCCESS Install all the resources success. + @retval other Error occur when install the resources. +**/ +EFI_STATUS +HiiInstall( + VOID + ); + +/** + Uninstall the HII capability. + + @retval EFI_SUCCESS Uninstall all the resources success. + @retval others Other errors occur when unistall the hii resource. +**/ +EFI_STATUS +HiiUninstall( + VOID + ); + +/** + Initialize the Opal disk base on the hardware info get from device. + + @param Dev The Opal device. + + @retval EFI_SUCESS Initialize the device success. + @retval EFI_DEVICE_ERROR Get info from device failed. + +**/ +EFI_STATUS +OpalDiskInitialize ( + IN OPAL_DRIVER_DEVICE *Dev + ); + +#endif // _HII_H_ diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiCallbacks.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiCallbacks.c new file mode 100644 index 0000000000..b07e38c144 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiCallbacks.c @@ -0,0 +1,219 @@ +/** @file + Callbacks required by the HII of the Opal UEFI Driver to help display + Opal device information. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalHii.h" + +/** + Get Opal var name. + The return Value must be freed by caller if not NULL + + @param OpalDisk The disk. + @param Prefix The prefix string. + + @retval The var name string. + +**/ +CHAR16* +OpalDriverGetOpalVarName( + OPAL_DISK *OpalDisk, + const CHAR16 *Prefix + ) +{ + OPAL_DRIVER_DEVICE* Dev; + UINTN PrefixLen; + UINTN NameLen; + UINTN VarNameLen; + CHAR16* VarName; + + Dev = DRIVER_DEVICE_FROM_OPALDISK(OpalDisk); + if (Dev == NULL) { + return NULL; + } + + PrefixLen = StrLen(Prefix); + + NameLen = 0; + if (Dev->Name16 != NULL) { + NameLen = StrLen(Dev->Name16); + } + + VarNameLen = PrefixLen + NameLen; + + VarName = (CHAR16*)AllocateZeroPool((VarNameLen + 1) * sizeof(CHAR16)); + if (VarName == NULL) { + return NULL; + } + + CopyMem(VarName, Prefix, PrefixLen * sizeof(CHAR16)); + if (Dev->Name16 != NULL) { + CopyMem(VarName + PrefixLen, Dev->Name16, NameLen * sizeof(CHAR16)); + } + VarName[VarNameLen] = 0; + + return VarName; +} + +/** + Get the driver image handle. + + @retval the driver image handle. + +**/ +EFI_HANDLE +HiiGetDriverImageHandleCB( + VOID + ) +{ + return gImageHandle; +} + +/** + Check whether enable feature or not. + + @retval Return the disk number. + +**/ +UINT8 +HiiGetNumConfigRequiredOpalDisksCB( + VOID + ) +{ + UINT8 NumDisks; + UINT8 NumLockedOpalDisks; + OPAL_DISK *OpalDisk; + UINT8 Index; + + NumLockedOpalDisks = 0; + + NumDisks = GetDeviceCount(); + + for (Index = 0; Index < NumDisks; Index++) { + OpalDisk = HiiGetOpalDiskCB(Index); + + if (OpalDisk != NULL) { + if (!OpalFeatureEnabled (&OpalDisk->SupportedAttributes, &OpalDisk->LockingFeature)) { + DEBUG ((DEBUG_INFO, "Ignoring disk %u because feature is disabled or health has already been inspected\n", Index)); + } else if (OpalDeviceLocked (&OpalDisk->SupportedAttributes, &OpalDisk->LockingFeature)) { + NumLockedOpalDisks++; + } + } + } + + return NumLockedOpalDisks; +} + + + +/** + Returns the opaque pointer to a physical disk context. + + @param DiskIndex Input the disk index. + + @retval The device pointer. + +**/ +VOID * +HiiGetDiskContextCB( + UINT8 DiskIndex + ) +{ + OPAL_DRIVER_DEVICE* Dev; + UINT8 CurrentDisk; + + Dev = OpalDriverGetDeviceList(); + CurrentDisk = 0; + + if (DiskIndex >= GetDeviceCount()) { + return NULL; + } + + while (Dev != NULL) { + if (CurrentDisk == DiskIndex) { + return Dev; + } else { + Dev = Dev->Next; + CurrentDisk++; + } + } + + return NULL; +} + +/** + Returns the opaque pointer to a physical disk context. + + @param DiskIndex Input the disk index. + + @retval The device pointer. + +**/ +OPAL_DISK* +HiiGetOpalDiskCB( + UINT8 DiskIndex + ) +{ + VOID *Ctx; + OPAL_DRIVER_DEVICE *Tmp; + + Ctx = HiiGetDiskContextCB (DiskIndex); + + if (Ctx == NULL) { + return NULL; + } + + Tmp = (OPAL_DRIVER_DEVICE*) Ctx; + + return &Tmp->OpalDisk; +} + +/** + Returns the disk name. + + @param DiskIndex Input the disk index. + + @retval Returns the disk name. + +**/ +CHAR8* +HiiDiskGetNameCB( + UINT8 DiskIndex + ) +{ + OPAL_DRIVER_DEVICE* Ctx; + + Ctx = (OPAL_DRIVER_DEVICE*) HiiGetDiskContextCB (DiskIndex); + + if (Ctx != NULL) { + if (Ctx->NameZ == NULL) { + OpalDriverGetDriverDeviceName (Ctx); + } + return Ctx->NameZ; + } + return NULL; +} + +/** + Returns the driver name. + + @retval Returns the driver name. + +**/ +CHAR16* +HiiGetDriverNameCB( + VOID + ) +{ + return (CHAR16*)EFI_DRIVER_NAME_UNICODE; +} diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormStrings.uni b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormStrings.uni new file mode 100644 index 0000000000..5f753dfa8c --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormStrings.uni @@ -0,0 +1,85 @@ +// /** @file +// +// String definitions for Setup formset. +// +// Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+// +// 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. +// +// **/ + +/=# +///////////////////////////////// GENERIC DEFINITIONS ///////////////////////////////// +#langdef en-US "English" +#string STR_NULL #language en-US " " + +///////////////////////////////// FORM SET ///////////////////////////////// +#string STR_FORM_SET_HELP #language en-US "Manage Opal disks" + +///////////////////////////////// MULTIPLE FORMS ///////////////////////////////// +#string STR_OPAL #language en-US "Opal" +#string STR_MAIN_OPAL_VERSION #language en-US "Version 00.0.0.0000" + +///////////////////////////////// MAIN MENU FORM ///////////////////////////////// +#string STR_MAIN_PHY_DISKS_LBL #language en-US "Physical Disks:" + +#string STR_MAIN_GOTO_DISK_INFO_0 #language en-US " " +#string STR_MAIN_GOTO_DISK_INFO_1 #language en-US " " +#string STR_MAIN_GOTO_DISK_INFO_2 #language en-US " " +#string STR_MAIN_GOTO_DISK_INFO_3 #language en-US " " +#string STR_MAIN_GOTO_DISK_INFO_4 #language en-US " " +#string STR_MAIN_GOTO_DISK_INFO_5 #language en-US " " + +#string STR_MAIN_GOTO_DISK_INFO_HELP #language en-US "Select to see Opal disk actions" + +#string STR_MAIN_NO_DISKS_PRESENT_LBL #language en-US "No disks connected to system" +#string STR_MAIN_NO_DISKS_PRESENT_LBL_HELP #language en-US "The storage needs to be connected before EndOfDxe" + +///////////////////////////////// DISK INFO MENU FORM ///////////////////////////////// +#string STR_DISK_INFO_SELECTED_DISK_NAME #language en-US " " + +#string STR_DISK_INFO_LOCK #language en-US "Lock" +#string STR_DISK_INFO_UNLOCK #language en-US "Unlock" +#string STR_DISK_INFO_SET_ADMIN_PSWD #language en-US "Update Drive Admin Password" +#string STR_DISK_INFO_SET_USER_PSWD #language en-US "Set Drive User Password" +#string STR_DISK_INFO_SECURE_ERASE #language en-US "Secure Erase User Data" +#string STR_DISK_INFO_PSID_REVERT #language en-US "PSID Revert to factory default" +#string STR_DISK_INFO_REVERT #language en-US "Admin Revert to factory default and Disable" +#string STR_DISK_INFO_DISABLE_USER #language en-US "Disable User" +#string STR_DISK_INFO_ENABLE_FEATURE #language en-US "Enable Feature" +#string STR_DISK_INFO_ENABLE_BLOCKSID #language en-US "TCG Storage Action" +#string STR_ENABLED #language en-US "Enable BlockSID" +#string STR_DISABLED #language en-US "Disable BlockSID" + +#string STR_NONE #language en-US "None" +#string STR_DISK_INFO_ENABLE_BLOCKSID_TRUE #language en-US "Require physical presence when remote enable BlockSID" +#string STR_DISK_INFO_ENABLE_BLOCKSID_FALSE #language en-US "Not require physical presence when remote enable BlockSID" +#string STR_DISK_INFO_DISABLE_BLOCKSID_TRUE #language en-US "Require physical presence when remote disable BlockSID" +#string STR_DISK_INFO_DISABLE_BLOCKSID_FALSE #language en-US "Not require physical presence when remote disable BlockSID" + +#string STR_BLOCKSID_STATUS_HELP #language en-US "BlockSID action change status" +#string STR_BLOCKSID_STATUS #language en-US "Current BlockSID Status:" +#string STR_BLOCKSID_STATUS1 #language en-US "" +#string STR_BLOCKSID_STATUS2 #language en-US "" +#string STR_BLOCKSID_STATUS3 #language en-US "" + +#string STR_OPAL_REQUESTS_LBL #language en-US "Opal Password Requests:" +#string STR_DISK_INFO_LOCK_HELP #language en-US "Lock the disk" +#string STR_DISK_INFO_UNLOCK_HELP #language en-US "Unlock the disk" +#string STR_DISK_INFO_SET_ADMIN_PSWD_HELP #language en-US "Set password for the administrator, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_SET_USER_PSWD_HELP #language en-US "Set password for User 1, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_SECURE_ERASE_HELP #language en-US "Securely erase the disk, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_REVERT_HELP #language en-US "Revert the disk to factory defaults, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_PSID_REVERT_HELP #language en-US "Revert the disk to factory defaults, PSID is a 32 character case sensitive value, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_DISABLE_USER_HELP #language en-US "Disable User, reset is required for the request to be processed in next boot" +#string STR_DISK_INFO_ENABLE_FEATURE_HELP #language en-US "Enable Feature, reset is required for the request to be processed in next boot" +#string STR_KEEP_USER_DATA_PROMPT #language en-US " Keep User Data" +#string STR_KEEP_USER_DATA_HELP #language en-US "Check to keep user data, otherwise data will be lost" + +#string STR_DISK_INFO_ENABLE_BLOCKSID_HELP #language en-US "Change BlockSID actions, includes enable or disable BlockSID, Require or not require physical presence when remote enable or disable BlockSID" diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormValues.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormValues.h new file mode 100644 index 0000000000..3ff7d4726d --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalHiiFormValues.h @@ -0,0 +1,123 @@ +/** @file + Defines Opal HII form ids, structures and values. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + + +#ifndef _OPAL_HII_FORM_VALUES_H_ +#define _OPAL_HII_FORM_VALUES_H_ + +// ID's for various forms that will be used by HII +#define FORMID_VALUE_MAIN_MENU 0x01 +#define FORMID_VALUE_DISK_INFO_FORM_MAIN 0x02 + +#define OPAL_REQUEST_VARIABLE_NAME L"OpalRequest" + +#pragma pack(1) +typedef struct { + UINT16 Lock:1; + UINT16 Unlock:1; + UINT16 SetAdminPwd:1; + UINT16 SetUserPwd:1; + UINT16 SecureErase:1; + UINT16 Revert:1; + UINT16 PsidRevert:1; + UINT16 DisableUser:1; + UINT16 DisableFeature:1; + UINT16 EnableFeature:1; + UINT16 Reserved:5; + UINT16 KeepUserData:1; +} OPAL_REQUEST; + +typedef struct { + UINT8 NumDisks; + UINT8 SelectedDiskIndex; + UINT16 SelectedDiskAvailableActions; + UINT16 SupportedDisks; + BOOLEAN KeepUserDataForced; + OPAL_REQUEST OpalRequest; + UINT8 EnableBlockSid; +} OPAL_HII_CONFIGURATION; + +typedef struct { + UINT32 Length; + OPAL_REQUEST OpalRequest; + //EFI_DEVICE_PATH_PROTOCOL OpalDevicePath; +} OPAL_REQUEST_VARIABLE; + +#pragma pack() + +/* Action Flags */ +#define HII_ACTION_NONE 0x0000 +#define HII_ACTION_LOCK 0x0001 +#define HII_ACTION_UNLOCK 0x0002 +#define HII_ACTION_SET_ADMIN_PWD 0x0004 +#define HII_ACTION_SET_USER_PWD 0x0008 +#define HII_ACTION_SECURE_ERASE 0x0010 +#define HII_ACTION_REVERT 0x0020 +#define HII_ACTION_PSID_REVERT 0x0040 +#define HII_ACTION_DISABLE_USER 0x0080 +#define HII_ACTION_DISABLE_FEATURE 0x0100 +#define HII_ACTION_ENABLE_FEATURE 0x0200 + +/* Number of bits allocated for each part of a unique key for an HII_ITEM + * all bits together must be <= 16 (EFI_QUESTION_ID is UINT16) + * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + * | |-----------------------| |---------------------------| + * FLG INDEX ID + */ +#define HII_KEY_ID_BITS 8 +#define HII_KEY_INDEX_BITS 7 +#define HII_KEY_FLAG_BITS 1 + +#define HII_KEY_FLAG 0x8000 // bit 15 (zero based) + +/***********/ +/* Key IDs */ +/***********/ + +#define HII_KEY_ID_GOTO_DISK_INFO 1 + +#define HII_KEY_ID_VAR_SUPPORTED_DISKS 2 +#define HII_KEY_ID_VAR_SELECTED_DISK_AVAILABLE_ACTIONS 3 + +#define HII_KEY_ID_BLOCKSID 4 +#define HII_KEY_ID_SET_ADMIN_PWD 5 +#define HII_KEY_ID_SET_USER_PWD 6 +#define HII_KEY_ID_SECURE_ERASE 7 +#define HII_KEY_ID_REVERT 8 +#define HII_KEY_ID_KEEP_USER_DATA 9 +#define HII_KEY_ID_PSID_REVERT 0xA +#define HII_KEY_ID_DISABLE_USER 0xB +#define HII_KEY_ID_ENABLE_FEATURE 0xC + +#define HII_KEY_ID_MAX 0xC // !!Update each time a new ID is added!! + +#define HII_KEY_WITH_INDEX(id, index) \ + ( \ + HII_KEY_FLAG | \ + (id) | \ + ((index) << HII_KEY_ID_BITS) \ + ) + +#define HII_KEY(id) HII_KEY_WITH_INDEX(id, 0) + +#define PACKAGE_LIST_GUID { 0xf0308176, 0x9058, 0x4153, { 0x93, 0x3d, 0xda, 0x2f, 0xdc, 0xc8, 0x3e, 0x44 } } + +/* {410483CF-F4F9-4ece-848A-1958FD31CEB7} */ +#define SETUP_FORMSET_GUID { 0x410483cf, 0xf4f9, 0x4ece, { 0x84, 0x8a, 0x19, 0x58, 0xfd, 0x31, 0xce, 0xb7 } } + +// {BBF1ACD2-28D8-44ea-A291-58A237FEDF1A} +#define SETUP_VARIABLE_GUID { 0xbbf1acd2, 0x28d8, 0x44ea, { 0xa2, 0x91, 0x58, 0xa2, 0x37, 0xfe, 0xdf, 0x1a } } + +#endif //_HII_FORM_VALUES_H_ + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.c new file mode 100644 index 0000000000..7657bb26e2 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.c @@ -0,0 +1,2144 @@ +/** @file + Provide functions to initialize NVME controller and perform NVME commands + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalPasswordPei.h" + + +#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1)) + 1) + +/// +/// NVME Host controller registers operation +/// +#define NVME_GET_CAP(Nvme, Cap) NvmeMmioRead (Cap, Nvme->Nbar + NVME_CAP_OFFSET, sizeof (NVME_CAP)) +#define NVME_GET_CC(Nvme, Cc) NvmeMmioRead (Cc, Nvme->Nbar + NVME_CC_OFFSET, sizeof (NVME_CC)) +#define NVME_SET_CC(Nvme, Cc) NvmeMmioWrite (Nvme->Nbar + NVME_CC_OFFSET, Cc, sizeof (NVME_CC)) +#define NVME_GET_CSTS(Nvme, Csts) NvmeMmioRead (Csts, Nvme->Nbar + NVME_CSTS_OFFSET, sizeof (NVME_CSTS)) +#define NVME_GET_AQA(Nvme, Aqa) NvmeMmioRead (Aqa, Nvme->Nbar + NVME_AQA_OFFSET, sizeof (NVME_AQA)) +#define NVME_SET_AQA(Nvme, Aqa) NvmeMmioWrite (Nvme->Nbar + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA)) +#define NVME_GET_ASQ(Nvme, Asq) NvmeMmioRead (Asq, Nvme->Nbar + NVME_ASQ_OFFSET, sizeof (NVME_ASQ)) +#define NVME_SET_ASQ(Nvme, Asq) NvmeMmioWrite (Nvme->Nbar + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ)) +#define NVME_GET_ACQ(Nvme, Acq) NvmeMmioRead (Acq, Nvme->Nbar + NVME_ACQ_OFFSET, sizeof (NVME_ACQ)) +#define NVME_SET_ACQ(Nvme, Acq) NvmeMmioWrite (Nvme->Nbar + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ)) +#define NVME_GET_VER(Nvme, Ver) NvmeMmioRead (Ver, Nvme->Nbar + NVME_VER_OFFSET, sizeof (NVME_VER)) +#define NVME_SET_SQTDBL(Nvme, Qid, Sqtdbl) NvmeMmioWrite (Nvme->Nbar + NVME_SQTDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL)) +#define NVME_SET_CQHDBL(Nvme, Qid, Cqhdbl) NvmeMmioWrite (Nvme->Nbar + NVME_CQHDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL)) + +/// +/// Base memory address +/// +enum { + BASEMEM_CONTROLLER_DATA, + BASEMEM_IDENTIFY_DATA, + BASEMEM_ASQ, + BASEMEM_ACQ, + BASEMEM_SQ, + BASEMEM_CQ, + BASEMEM_PRP, + BASEMEM_SECURITY, + MAX_BASEMEM_COUNT +}; + +/// +/// All of base memories are 4K(0x1000) alignment +/// +#define NVME_MEM_BASE(Nvme) ((UINTN)(Nvme->BaseMem)) +#define NVME_CONTROL_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CONTROLLER_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_NAMESPACE_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_IDENTIFY_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_ASQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ASQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_ACQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ACQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_SQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_CQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_PRP_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_PRP) + ((index)*NVME_PRP_SIZE)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) +#define NVME_SEC_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SECURITY)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE)) + +/** + Transfer MMIO Data to memory. + + @param[in,out] MemBuffer - Destination: Memory address + @param[in] MmioAddr - Source: MMIO address + @param[in] Size - Size for read + + @retval EFI_SUCCESS - MMIO read sucessfully +**/ +EFI_STATUS +NvmeMmioRead ( + IN OUT VOID *MemBuffer, + IN UINTN MmioAddr, + IN UINTN Size + ) +{ + UINTN Offset; + UINT8 Data; + UINT8 *Ptr; + + // priority has adjusted + switch (Size) { + case 4: + *((UINT32 *)MemBuffer) = MmioRead32 (MmioAddr); + break; + + case 8: + *((UINT64 *)MemBuffer) = MmioRead64 (MmioAddr); + break; + + case 2: + *((UINT16 *)MemBuffer) = MmioRead16 (MmioAddr); + break; + + case 1: + *((UINT8 *)MemBuffer) = MmioRead8 (MmioAddr); + break; + + default: + Ptr = (UINT8 *)MemBuffer; + for (Offset = 0; Offset < Size; Offset += 1) { + Data = MmioRead8 (MmioAddr + Offset); + Ptr[Offset] = Data; + } + break; + } + + return EFI_SUCCESS; +} + +/** + Transfer memory data to MMIO. + + @param[in,out] MmioAddr - Destination: MMIO address + @param[in] MemBuffer - Source: Memory address + @param[in] Size - Size for write + + @retval EFI_SUCCESS - MMIO write sucessfully +**/ +EFI_STATUS +NvmeMmioWrite ( + IN OUT UINTN MmioAddr, + IN VOID *MemBuffer, + IN UINTN Size + ) +{ + UINTN Offset; + UINT8 Data; + UINT8 *Ptr; + + // priority has adjusted + switch (Size) { + case 4: + MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer)); + break; + + case 8: + MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer)); + break; + + case 2: + MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer)); + break; + + case 1: + MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer)); + break; + + default: + Ptr = (UINT8 *)MemBuffer; + for (Offset = 0; Offset < Size; Offset += 1) { + Data = Ptr[Offset]; + MmioWrite8 (MmioAddr + Offset, Data); + } + break; + } + + return EFI_SUCCESS; +} + +/** + Transfer MMIO data to memory. + + @param[in,out] MemBuffer - Destination: Memory address + @param[in] MmioAddr - Source: MMIO address + @param[in] Size - Size for read + + @retval EFI_SUCCESS - MMIO read sucessfully +**/ +EFI_STATUS +OpalPciRead ( + IN OUT VOID *MemBuffer, + IN UINTN MmioAddr, + IN UINTN Size + ) +{ + UINTN Offset; + UINT8 Data; + UINT8 *Ptr; + + // priority has adjusted + switch (Size) { + case 4: + *((UINT32 *)MemBuffer) = PciRead32 (MmioAddr); + break; + + case 2: + *((UINT16 *)MemBuffer) = PciRead16 (MmioAddr); + break; + + case 1: + *((UINT8 *)MemBuffer) = PciRead8 (MmioAddr); + break; + + default: + Ptr = (UINT8 *)MemBuffer; + for (Offset = 0; Offset < Size; Offset += 1) { + Data = PciRead8 (MmioAddr + Offset); + Ptr[Offset] = Data; + } + break; + } + + return EFI_SUCCESS; +} + +/** + Transfer memory data to MMIO. + + @param[in,out] MmioAddr - Destination: MMIO address + @param[in] MemBuffer - Source: Memory address + @param[in] Size - Size for write + + @retval EFI_SUCCESS - MMIO write sucessfully +**/ +EFI_STATUS +OpalPciWrite ( + IN OUT UINTN MmioAddr, + IN VOID *MemBuffer, + IN UINTN Size + ) +{ + UINTN Offset; + UINT8 Data; + UINT8 *Ptr; + + // priority has adjusted + switch (Size) { + case 4: + PciWrite32 (MmioAddr, *((UINT32 *)MemBuffer)); + break; + + case 2: + PciWrite16 (MmioAddr, *((UINT16 *)MemBuffer)); + break; + + case 1: + PciWrite8 (MmioAddr, *((UINT8 *)MemBuffer)); + break; + + default: + Ptr = (UINT8 *)MemBuffer; + for (Offset = 0; Offset < Size; Offset += 1) { + Data = Ptr[Offset]; + PciWrite8 (MmioAddr + Offset, Data); + } + break; + } + + return EFI_SUCCESS; +} + +/** + Get total pages for specific NVME based memory. + + @param[in] BaseMemIndex - The Index of BaseMem (0-based). + + @retval - The page count for specific BaseMem Index + +**/ +UINT32 +NvmeGetBaseMemPages ( + IN UINTN BaseMemIndex + ) +{ + UINT32 Pages; + UINTN Index; + UINT32 PageSizeList[8]; + + PageSizeList[0] = 1; /* Controller Data */ + PageSizeList[1] = 1; /* Identify Data */ + PageSizeList[2] = 1; /* ASQ */ + PageSizeList[3] = 1; /* ACQ */ + PageSizeList[4] = 1; /* SQs */ + PageSizeList[5] = 1; /* CQs */ + PageSizeList[6] = NVME_PRP_SIZE * NVME_CSQ_DEPTH; /* PRPs */ + PageSizeList[7] = 1; /* Security Commands */ + + if (BaseMemIndex > MAX_BASEMEM_COUNT) { + ASSERT (FALSE); + return 0; + } + + Pages = 0; + for (Index = 0; Index < BaseMemIndex; Index++) { + Pages += PageSizeList[Index]; + } + + return Pages; +} + +/** + Wait for NVME controller status to be ready or not. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] WaitReady - Flag for waitting status ready or not + + @return EFI_SUCCESS - Successfully to wait specific status. + @return others - Fail to wait for specific controller status. + +**/ +STATIC +EFI_STATUS +NvmeWaitController ( + IN NVME_CONTEXT *Nvme, + IN BOOLEAN WaitReady + ) +{ + NVME_CSTS Csts; + EFI_STATUS Status; + UINT32 Index; + UINT8 Timeout; + + // + // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after + // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To. + // + if (Nvme->Cap.To == 0) { + Timeout = 1; + } else { + Timeout = Nvme->Cap.To; + } + + Status = EFI_SUCCESS; + for(Index = (Timeout * 500); Index != 0; --Index) { + MicroSecondDelay (1000); + + // + // Check if the controller is initialized + // + Status = NVME_GET_CSTS (Nvme, &Csts); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status)); + return Status; + } + + if ((BOOLEAN) Csts.Rdy == WaitReady) { + break; + } + } + + if (Index == 0) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Disable the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully disable the controller. + @return others - Fail to disable the controller. + +**/ +STATIC +EFI_STATUS +NvmeDisableController ( + IN NVME_CONTEXT *Nvme + ) +{ + NVME_CC Cc; + NVME_CSTS Csts; + EFI_STATUS Status; + + Status = NVME_GET_CSTS (Nvme, &Csts); + + /// + /// Read Controller Configuration Register. + /// + Status = NVME_GET_CC (Nvme, &Cc); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status)); + goto Done; + } + + if (Cc.En == 1) { + Cc.En = 0; + /// + /// Disable the controller. + /// + Status = NVME_SET_CC (Nvme, &Cc); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status)); + goto Done; + } + } + + Status = NvmeWaitController (Nvme, FALSE); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status)); + goto Done; + } + + return EFI_SUCCESS; + +Done: + DEBUG ((DEBUG_INFO, "NvmeDisableController fail, Status: %r\n", Status)); + return Status; +} + +/** + Enable the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully enable the controller. + @return EFI_DEVICE_ERROR - Fail to enable the controller. + @return EFI_TIMEOUT - Fail to enable the controller in given time slot. + +**/ +STATIC +EFI_STATUS +NvmeEnableController ( + IN NVME_CONTEXT *Nvme + ) +{ + NVME_CC Cc; + EFI_STATUS Status; + + // + // Enable the controller + // + ZeroMem (&Cc, sizeof (NVME_CC)); + Cc.En = 1; + Cc.Iosqes = 6; + Cc.Iocqes = 4; + Status = NVME_SET_CC (Nvme, &Cc); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status)); + goto Done; + } + + Status = NvmeWaitController (Nvme, TRUE); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status)); + goto Done; + } + + return EFI_SUCCESS; + +Done: + DEBUG ((DEBUG_INFO, "NvmeEnableController fail, Status: %r\n", Status)); + return Status; +} + +/** + Shutdown the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully shutdown the controller. + @return EFI_DEVICE_ERROR - Fail to shutdown the controller. + @return EFI_TIMEOUT - Fail to shutdown the controller in given time slot. + +**/ +STATIC +EFI_STATUS +NvmeShutdownController ( + IN NVME_CONTEXT *Nvme + ) +{ + NVME_CC Cc; + NVME_CSTS Csts; + EFI_STATUS Status; + UINT32 Index; + UINTN Timeout; + + Status = NVME_GET_CC (Nvme, &Cc); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status)); + return Status; + } + + Cc.Shn = 1; // Normal shutdown + + Status = NVME_SET_CC (Nvme, &Cc); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status)); + return Status; + } + + Timeout = NVME_GENERIC_TIMEOUT/1000; // ms + for(Index = (UINT32)(Timeout); Index != 0; --Index) { + MicroSecondDelay (1000); + + Status = NVME_GET_CSTS (Nvme, &Csts); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status)); + return Status; + } + + if (Csts.Shst == 2) { // Shutdown processing complete + break; + } + } + + if (Index == 0) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Check the execution status from a given completion queue entry. + + @param[in] Cq - A pointer to the NVME_CQ item. + +**/ +EFI_STATUS +NvmeCheckCqStatus ( + IN NVME_CQ *Cq + ) +{ + if (Cq->Sct == 0x0 && Cq->Sc == 0x0) { + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN)Cq)); + DEBUG ((DEBUG_INFO, " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid)); + DEBUG ((DEBUG_INFO, " NVMe Cmd Execution Result - ")); + + switch (Cq->Sct) { + case 0x0: + switch (Cq->Sc) { + case 0x0: + DEBUG ((DEBUG_INFO, "Successful Completion\n")); + return EFI_SUCCESS; + case 0x1: + DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n")); + break; + case 0x2: + DEBUG ((DEBUG_INFO, "Invalid Field in Command\n")); + break; + case 0x3: + DEBUG ((DEBUG_INFO, "Command ID Conflict\n")); + break; + case 0x4: + DEBUG ((DEBUG_INFO, "Data Transfer Error\n")); + break; + case 0x5: + DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss Notification\n")); + break; + case 0x6: + DEBUG ((DEBUG_INFO, "Internal Device Error\n")); + break; + case 0x7: + DEBUG ((DEBUG_INFO, "Command Abort Requested\n")); + break; + case 0x8: + DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n")); + break; + case 0x9: + DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused Command\n")); + break; + case 0xA: + DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused Command\n")); + break; + case 0xB: + DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n")); + break; + case 0xC: + DEBUG ((DEBUG_INFO, "Command Sequence Error\n")); + break; + case 0xD: + DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n")); + break; + case 0xE: + DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n")); + break; + case 0xF: + DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n")); + break; + case 0x10: + DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n")); + break; + case 0x11: + DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n")); + break; + case 0x80: + DEBUG ((DEBUG_INFO, "LBA Out of Range\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Capacity Exceeded\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "Namespace Not Ready\n")); + break; + case 0x83: + DEBUG ((DEBUG_INFO, "Reservation Conflict\n")); + break; + } + break; + + case 0x1: + switch (Cq->Sc) { + case 0x0: + DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n")); + break; + case 0x1: + DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n")); + break; + case 0x2: + DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n")); + break; + case 0x3: + DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n")); + break; + case 0x5: + DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n")); + break; + case 0x6: + DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n")); + break; + case 0x7: + DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n")); + break; + case 0x8: + DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n")); + break; + case 0x9: + DEBUG ((DEBUG_INFO, "Invalid Log Page\n")); + break; + case 0xA: + DEBUG ((DEBUG_INFO, "Invalid Format\n")); + break; + case 0xB: + DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional Reset\n")); + break; + case 0xC: + DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n")); + break; + case 0xD: + DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n")); + break; + case 0xE: + DEBUG ((DEBUG_INFO, "Feature Not Changeable\n")); + break; + case 0xF: + DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n")); + break; + case 0x10: + DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem Reset\n")); + break; + case 0x80: + DEBUG ((DEBUG_INFO, "Conflicting Attributes\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Invalid Protection Information\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n")); + break; + } + break; + + case 0x2: + switch (Cq->Sc) { + case 0x80: + DEBUG ((DEBUG_INFO, "Write Fault\n")); + break; + case 0x81: + DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n")); + break; + case 0x82: + DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n")); + break; + case 0x83: + DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n")); + break; + case 0x84: + DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n")); + break; + case 0x85: + DEBUG ((DEBUG_INFO, "Compare Failure\n")); + break; + case 0x86: + DEBUG ((DEBUG_INFO, "Access Denied\n")); + break; + } + break; + + default: + DEBUG ((DEBUG_INFO, "Unknown error\n")); + break; + } + + return EFI_DEVICE_ERROR; +} + +/** + Create PRP lists for Data transfer which is larger than 2 memory pages. + Note here we calcuate the number of required PRP lists and allocate them at one time. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] SqId - The SQ index for this PRP + @param[in] PhysicalAddr - The physical base address of Data Buffer. + @param[in] Pages - The number of pages to be transfered. + @param[out] PrpListHost - The host base address of PRP lists. + @param[in,out] PrpListNo - The number of PRP List. + + @retval The pointer Value to the first PRP List of the PRP lists. + +**/ +STATIC +UINT64 +NvmeCreatePrpList ( + IN NVME_CONTEXT *Nvme, + IN UINT16 SqId, + IN EFI_PHYSICAL_ADDRESS PhysicalAddr, + IN UINTN Pages, + OUT VOID **PrpListHost, + IN OUT UINTN *PrpListNo + ) +{ + UINTN PrpEntryNo; + UINT64 PrpListBase; + UINTN PrpListIndex; + UINTN PrpEntryIndex; + UINT64 Remainder; + EFI_PHYSICAL_ADDRESS PrpListPhyAddr; + UINTN Bytes; + UINT8 *PrpEntry; + EFI_PHYSICAL_ADDRESS NewPhyAddr; + + /// + /// The number of Prp Entry in a memory page. + /// + PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64); + + /// + /// Calculate total PrpList number. + /// + *PrpListNo = (UINTN) DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder); + if (Remainder != 0) { + *PrpListNo += 1; + } + + if (*PrpListNo > NVME_PRP_SIZE) { + DEBUG ((DEBUG_INFO, "NvmeCreatePrpList (PhysicalAddr: %lx, Pages: %x) PrpEntryNo: %x\n", + PhysicalAddr, Pages, PrpEntryNo)); + DEBUG ((DEBUG_INFO, "*PrpListNo: %x, Remainder: %lx", *PrpListNo, Remainder)); + ASSERT (FALSE); + } + *PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Nvme, SqId); + + Bytes = EFI_PAGES_TO_SIZE (*PrpListNo); + PrpListPhyAddr = (UINT64)(UINTN)(*PrpListHost); + + /// + /// Fill all PRP lists except of last one. + /// + ZeroMem (*PrpListHost, Bytes); + for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) { + PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; + + for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) { + PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64)); + if (PrpEntryIndex != PrpEntryNo - 1) { + /// + /// Fill all PRP entries except of last one. + /// + CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64)); + PhysicalAddr += EFI_PAGE_SIZE; + } else { + /// + /// Fill last PRP entries with next PRP List pointer. + /// + NewPhyAddr = (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE); + CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof (UINT64)); + } + } + } + + /// + /// Fill last PRP list. + /// + PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE; + for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) { + PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64)); + CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64)); + + PhysicalAddr += EFI_PAGE_SIZE; + } + + return PrpListPhyAddr; +} + +/** + Check whether there are available command slots. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - Available command slot is found + @retval EFI_NOT_READY - No available command slot is found + @retval EFI_DEVICE_ERROR - Error occurred on device side. + +**/ +EFI_STATUS +NvmeHasFreeCmdSlot ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ) +{ + return TRUE; +} + +/** + Check whether all command slots are clean. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - All command slots are clean + @retval EFI_NOT_READY - Not all command slots are clean + @retval EFI_DEVICE_ERROR - Error occurred on device side. + +**/ +EFI_STATUS +NvmeIsAllCmdSlotClean ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ) +{ + return EFI_SUCCESS; +} + +/** + Waits until all NVME commands completed. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - All NVME commands have completed + @retval EFI_TIMEOUT - Timeout occured + @retval EFI_NOT_READY - Not all NVME commands have completed + @retval others - Error occurred on device side. +**/ +EFI_STATUS +NvmeWaitAllComplete ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ) +{ + return EFI_SUCCESS; +} + +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking + I/O functionality is optional. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] NamespaceId - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent. + A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace + ID specifies that the command packet should be sent to all valid namespaces. + @param[in] NamespaceUuid - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent. + A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace + UUID specifies that the command packet should be sent to all valid namespaces. + @param[in,out] Packet - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified + by NamespaceId. + + @retval EFI_SUCCESS - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_NOT_READY - The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR - A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED - The command described by the NVM Express Command Packet is not supported by the host adapter. + The NVM Express Command Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT - A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +EFI_STATUS +NvmePassThru ( + IN NVME_CONTEXT *Nvme, + IN UINT32 NamespaceId, + IN UINT64 NamespaceUuid, + IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet + ) +{ + EFI_STATUS Status; + NVME_SQ *Sq; + NVME_CQ *Cq; + UINT8 Qid; + UINT32 Bytes; + UINT32 Offset; + EFI_PHYSICAL_ADDRESS PhyAddr; + VOID *PrpListHost; + UINTN PrpListNo; + UINT32 Timer; + UINTN SqSize; + UINTN CqSize; + + /// + /// check the Data fields in Packet parameter. + /// + if ((Nvme == NULL) || (Packet == NULL)) { + DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: Nvme(%x)/Packet(%x)\n", + (UINTN)Nvme, (UINTN)Packet)); + return EFI_INVALID_PARAMETER; + } + + if ((Packet->NvmeCmd == NULL) || (Packet->NvmeResponse == NULL)) { + DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: NvmeCmd(%x)/NvmeResponse(%x)\n", + (UINTN)Packet->NvmeCmd, (UINTN)Packet->NvmeResponse)); + return EFI_INVALID_PARAMETER; + } + + if (Packet->QueueId != NVME_ADMIN_QUEUE && Packet->QueueId != NVME_IO_QUEUE) { + DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: QueueId(%x)\n", + Packet->QueueId)); + return EFI_INVALID_PARAMETER; + } + + PrpListHost = NULL; + PrpListNo = 0; + Status = EFI_SUCCESS; + + Qid = Packet->QueueId; + Sq = Nvme->SqBuffer[Qid] + Nvme->SqTdbl[Qid].Sqt; + Cq = Nvme->CqBuffer[Qid] + Nvme->CqHdbl[Qid].Cqh; + if (Qid == NVME_ADMIN_QUEUE) { + SqSize = NVME_ASQ_SIZE + 1; + CqSize = NVME_ACQ_SIZE + 1; + } else { + SqSize = NVME_CSQ_DEPTH; + CqSize = NVME_CCQ_DEPTH; + } + + if (Packet->NvmeCmd->Nsid != NamespaceId) { + DEBUG ((DEBUG_ERROR, "NvmePassThru: Nsid mismatch (%x, %x)\n", + Packet->NvmeCmd->Nsid, NamespaceId)); + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Sq, sizeof (NVME_SQ)); + Sq->Opc = Packet->NvmeCmd->Cdw0.Opcode; + Sq->Fuse = Packet->NvmeCmd->Cdw0.FusedOperation; + Sq->Cid = Packet->NvmeCmd->Cdw0.Cid; + Sq->Nsid = Packet->NvmeCmd->Nsid; + + /// + /// Currently we only support PRP for Data transfer, SGL is NOT supported. + /// + ASSERT (Sq->Psdt == 0); + if (Sq->Psdt != 0) { + DEBUG ((DEBUG_ERROR, "NvmePassThru: doesn't support SGL mechanism\n")); + return EFI_UNSUPPORTED; + } + + Sq->Prp[0] = Packet->TransferBuffer; + Sq->Prp[1] = 0; + + if(Packet->MetadataBuffer != (UINT64)(UINTN)NULL) { + Sq->Mptr = Packet->MetadataBuffer; + } + + /// + /// If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps), + /// then build a PRP list in the second PRP submission queue entry. + /// + Offset = ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1); + Bytes = Packet->TransferLength; + + if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) { + /// + /// Create PrpList for remaining Data Buffer. + /// + PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1); + Sq->Prp[1] = NvmeCreatePrpList (Nvme, Nvme->SqTdbl[Qid].Sqt, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo); + if (Sq->Prp[1] == 0) { + Status = EFI_OUT_OF_RESOURCES; + DEBUG ((DEBUG_ERROR, "NvmeCreatePrpList fail, Status: %r\n", Status)); + goto EXIT; + } + + } else if ((Offset + Bytes) > EFI_PAGE_SIZE) { + Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1); + } + + if(Packet->NvmeCmd->Flags & CDW10_VALID) { + Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10; + } + if(Packet->NvmeCmd->Flags & CDW11_VALID) { + Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11; + } + if(Packet->NvmeCmd->Flags & CDW12_VALID) { + Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12; + } + if(Packet->NvmeCmd->Flags & CDW13_VALID) { + Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13; + } + if(Packet->NvmeCmd->Flags & CDW14_VALID) { + Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14; + } + if(Packet->NvmeCmd->Flags & CDW15_VALID) { + Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15; + } + + /// + /// Ring the submission queue doorbell. + /// + Nvme->SqTdbl[Qid].Sqt++; + if(Nvme->SqTdbl[Qid].Sqt == SqSize) { + Nvme->SqTdbl[Qid].Sqt = 0; + } + Status = NVME_SET_SQTDBL (Nvme, Qid, &Nvme->SqTdbl[Qid]); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NVME_SET_SQTDBL fail, Status: %r\n", Status)); + goto EXIT; + } + + /// + /// Wait for completion queue to get filled in. + /// + Status = EFI_TIMEOUT; + Timer = 0; + while (Timer < NVME_CMD_TIMEOUT) { + //DEBUG ((DEBUG_VERBOSE, "Timer: %x, Cq:\n", Timer)); + //DumpMem (Cq, sizeof (NVME_CQ)); + if (Cq->Pt != Nvme->Pt[Qid]) { + Status = EFI_SUCCESS; + break; + } + + MicroSecondDelay (NVME_CMD_WAIT); + Timer += NVME_CMD_WAIT; + } + + Nvme->CqHdbl[Qid].Cqh++; + if (Nvme->CqHdbl[Qid].Cqh == CqSize) { + Nvme->CqHdbl[Qid].Cqh = 0; + Nvme->Pt[Qid] ^= 1; + } + + /// + /// Copy the Respose Queue entry for this command to the callers response Buffer + /// + CopyMem (Packet->NvmeResponse, Cq, sizeof(NVM_EXPRESS_RESPONSE)); + + if (!EFI_ERROR(Status)) { // We still need to check CQ status if no timeout error occured + Status = NvmeCheckCqStatus (Cq); + } + NVME_SET_CQHDBL (Nvme, Qid, &Nvme->CqHdbl[Qid]); + +EXIT: + return Status; +} + +/** + Get identify controller Data. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Buffer - The Buffer used to store the identify controller Data. + + @return EFI_SUCCESS - Successfully get the identify controller Data. + @return others - Fail to get the identify controller Data. + +**/ +STATIC +EFI_STATUS +NvmeIdentifyController ( + IN NVME_CONTEXT *Nvme, + IN VOID *Buffer + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + // + // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. + // For the Identify command, the Namespace Identifier is only used for the Namespace Data structure. + // + Command.Nsid = 0; + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer; + CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + // + // Set bit 0 (Cns bit) to 1 to identify a controller + // + Command.Cdw10 = 1; + Command.Flags = CDW10_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Get specified identify namespace Data. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] NamespaceId - The specified namespace identifier. + @param[in] Buffer - The Buffer used to store the identify namespace Data. + + @return EFI_SUCCESS - Successfully get the identify namespace Data. + @return others - Fail to get the identify namespace Data. + +**/ +STATIC +EFI_STATUS +NvmeIdentifyNamespace ( + IN NVME_CONTEXT *Nvme, + IN UINT32 NamespaceId, + IN VOID *Buffer + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + Command.Nsid = NamespaceId; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer; + CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + // + // Set bit 0 (Cns bit) to 1 to identify a namespace + // + CommandPacket.NvmeCmd->Cdw10 = 0; + CommandPacket.NvmeCmd->Flags = CDW10_VALID; + + Status = NvmePassThru ( + Nvme, + NamespaceId, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Get Block Size for specific namespace of NVME. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return - Block Size in bytes + +**/ +STATIC +UINT32 +NvmeGetBlockSize ( + IN NVME_CONTEXT *Nvme + ) +{ + UINT32 BlockSize; + UINT32 Lbads; + UINT32 Flbas; + UINT32 LbaFmtIdx; + + Flbas = Nvme->NamespaceData->Flbas; + LbaFmtIdx = Flbas & 3; + Lbads = Nvme->NamespaceData->LbaFormat[LbaFmtIdx].Lbads; + + BlockSize = (UINT32)1 << Lbads; + return BlockSize; +} + +/** + Get last LBA for specific namespace of NVME. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return - Last LBA address + +**/ +STATIC +EFI_LBA +NvmeGetLastLba ( + IN NVME_CONTEXT *Nvme + ) +{ + EFI_LBA LastBlock; + LastBlock = Nvme->NamespaceData->Nsze - 1; + return LastBlock; +} + +/** + Create io completion queue. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully create io completion queue. + @return others - Fail to create io completion queue. + +**/ +STATIC +EFI_STATUS +NvmeCreateIoCompletionQueue ( + IN NVME_CONTEXT *Nvme + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + NVME_ADMIN_CRIOCQ CrIoCq; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + + CrIoCq.Qid = NVME_IO_QUEUE; + CrIoCq.Qsize = NVME_CCQ_SIZE; + CrIoCq.Pc = 1; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Create io submission queue. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully create io submission queue. + @return others - Fail to create io submission queue. + +**/ +STATIC +EFI_STATUS +NvmeCreateIoSubmissionQueue ( + IN NVME_CONTEXT *Nvme + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + NVME_ADMIN_CRIOSQ CrIoSq; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + + CrIoSq.Qid = NVME_IO_QUEUE; + CrIoSq.Qsize = NVME_CSQ_SIZE; + CrIoSq.Pc = 1; + CrIoSq.Cqid = NVME_IO_QUEUE; + CrIoSq.Qprio = 0; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Security send and receive commands. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] SendCommand - The flag to indicate the command type, TRUE for Send command and FALSE for receive command + @param[in] SecurityProtocol - Security Protocol + @param[in] SpSpecific - Security Protocol Specific + @param[in] TransferLength - Transfer Length of Buffer (in bytes) - always a multiple of 512 + @param[in,out] TransferBuffer - Address of Data to transfer + + @return EFI_SUCCESS - Successfully create io submission queue. + @return others - Fail to send/receive commands. + +**/ +EFI_STATUS +NvmeSecuritySendReceive ( + IN NVME_CONTEXT *Nvme, + IN BOOLEAN SendCommand, + IN UINT8 SecurityProtocol, + IN UINT16 SpSpecific, + IN UINTN TransferLength, + IN OUT VOID *TransferBuffer + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + NVME_ADMIN_SECSEND SecSend; + OACS *Oacs; + UINT8 Opcode; + VOID* *SecBuff; + + Oacs = (OACS *)&Nvme->ControllerData->Oacs; + + // + // Verify security bit for Security Send/Receive commands + // + if (Oacs->Security == 0) { + DEBUG ((DEBUG_ERROR, "Security command doesn't support.\n")); + return EFI_NOT_READY; + } + + SecBuff = (VOID *)(UINTN) NVME_SEC_BASE (Nvme); + + // + // Actions for sending security command + // + if (SendCommand) { + CopyMem (SecBuff, TransferBuffer, TransferLength); + } + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + ZeroMem (&SecSend, sizeof(NVME_ADMIN_SECSEND)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Opcode = (UINT8)(SendCommand ? NVME_ADMIN_SECURITY_SEND_OPC : NVME_ADMIN_SECURITY_RECV_OPC); + Command.Cdw0.Opcode = Opcode; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + CommandPacket.TransferBuffer = (UINT64)(UINTN)SecBuff; + CommandPacket.TransferLength = (UINT32)TransferLength; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + + SecSend.Spsp = SpSpecific; + SecSend.Secp = SecurityProtocol; + SecSend.Tl = (UINT32)TransferLength; + + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &SecSend, sizeof (NVME_ADMIN_SECSEND)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + // + // Actions for receiving security command + // + if (!SendCommand) { + CopyMem (TransferBuffer, SecBuff, TransferLength); + } + + return Status; +} + +/** + Destroy io completion queue. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully destroy io completion queue. + @return others - Fail to destroy io completion queue. + +**/ +STATIC +EFI_STATUS +NvmeDestroyIoCompletionQueue ( + IN NVME_CONTEXT *Nvme + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + NVME_ADMIN_DEIOCQ DelIoCq; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + ZeroMem (&DelIoCq, sizeof(NVME_ADMIN_DEIOCQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Command.Cdw0.Opcode = NVME_ADMIN_DELIOCQ_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + + DelIoCq.Qid = NVME_IO_QUEUE; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoCq, sizeof (NVME_ADMIN_DEIOCQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Destroy io submission queue. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @return EFI_SUCCESS - Successfully destroy io submission queue. + @return others - Fail to destroy io submission queue. + +**/ +STATIC +EFI_STATUS +NvmeDestroyIoSubmissionQueue ( + IN NVME_CONTEXT *Nvme + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + NVME_ADMIN_DEIOSQ DelIoSq; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + ZeroMem (&DelIoSq, sizeof(NVME_ADMIN_DEIOSQ)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + Command.Cdw0.Opcode = NVME_ADMIN_DELIOSQ_OPC; + Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++; + CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE]; + CommandPacket.TransferLength = EFI_PAGE_SIZE; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_ADMIN_QUEUE; + + DelIoSq.Qid = NVME_IO_QUEUE; + CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoSq, sizeof (NVME_ADMIN_DEIOSQ)); + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID; + + Status = NvmePassThru ( + Nvme, + NVME_CONTROLLER_ID, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Allocate transfer-related Data struct which is used at Nvme. + + @param[in, out] Nvme The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_OUT_OF_RESOURCE No enough resource. + @retval EFI_SUCCESS Successful to allocate resource. + +**/ +EFI_STATUS +EFIAPI +NvmeAllocateResource ( + IN OUT NVME_CONTEXT *Nvme + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS DeviceAddress; + VOID *Base; + VOID *Mapping; + + // + // Allocate resources for DMA. + // + Status = IoMmuAllocateBuffer ( + EFI_SIZE_TO_PAGES (NVME_MEM_MAX_SIZE), + &Base, + &DeviceAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base)); + Nvme->BaseMemMapping = Mapping; + Nvme->BaseMem = Base; + ZeroMem (Nvme->BaseMem, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (NVME_MEM_MAX_SIZE)); + + DEBUG (( + DEBUG_INFO, + "%a() NvmeContext 0x%x\n", + __FUNCTION__, + Nvme->BaseMem + )); + + return EFI_SUCCESS; +} + +/** + Free allocated transfer-related Data struct which is used at NVMe. + + @param[in, out] Nvme The pointer to the NVME_CONTEXT Data structure. + +**/ +VOID +EFIAPI +NvmeFreeResource ( + IN OUT NVME_CONTEXT *Nvme + ) +{ + if (Nvme->BaseMem != NULL) { + IoMmuFreeBuffer ( + EFI_SIZE_TO_PAGES (NVME_MEM_MAX_SIZE), + Nvme->BaseMem, + Nvme->BaseMemMapping + ); + Nvme->BaseMem = NULL; + } +} + +/** + Initialize the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - The NVM Express Controller is initialized successfully. + @retval Others - A device error occurred while initializing the controller. + +**/ +EFI_STATUS +NvmeControllerInit ( + IN NVME_CONTEXT *Nvme + ) +{ + EFI_STATUS Status; + NVME_AQA Aqa; + NVME_ASQ Asq; + NVME_ACQ Acq; + NVME_VER Ver; + + UINT32 MlBAR; + UINT32 MuBAR; + + /// + /// Update PCIE BAR0/1 for NVME device + /// + MlBAR = Nvme->Nbar; + MuBAR = 0; + PciWrite32 (Nvme->PciBase + 0x10, MlBAR); // MLBAR (BAR0) + PciWrite32 (Nvme->PciBase + 0x14, MuBAR); // MUBAR (BAR1) + + /// + /// Enable PCIE decode + /// + PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x6); + + // Version + NVME_GET_VER (Nvme, &Ver); + if (!(Ver.Mjr == 0x0001) && (Ver.Mnr == 0x0000)) { + DEBUG ((DEBUG_INFO, "\n!!!\n!!! NVME Version mismatch for the implementation !!!\n!!!\n")); + } + + /// + /// Read the Controller Capabilities register and verify that the NVM command set is supported + /// + Status = NVME_GET_CAP (Nvme, &Nvme->Cap); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "NVME_GET_CAP fail, Status: %r\n", Status)); + goto Done; + } + + if (Nvme->Cap.Css != 0x01) { + DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: the controller doesn't support NVMe command set\n")); + Status = EFI_UNSUPPORTED; + goto Done; + } + + /// + /// Currently the driver only supports 4k page Size. + /// + if ((Nvme->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) { + DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: only supports 4k page Size\n")); + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + goto Done; + } + + Nvme->Cid[0] = 0; + Nvme->Cid[1] = 0; + + Nvme->Pt[0] = 0; + Nvme->Pt[1] = 0; + + ZeroMem ((VOID *)(UINTN)(&(Nvme->SqTdbl[0])), sizeof (NVME_SQTDBL) * NVME_MAX_IO_QUEUES); + ZeroMem ((VOID *)(UINTN)(&(Nvme->CqHdbl[0])), sizeof (NVME_CQHDBL) * NVME_MAX_IO_QUEUES); + + ZeroMem (Nvme->BaseMem, NVME_MEM_MAX_SIZE); + + Status = NvmeDisableController (Nvme); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeDisableController fail, Status: %r\n", Status)); + goto Done; + } + + /// + /// set number of entries admin submission & completion queues. + /// + Aqa.Asqs = NVME_ASQ_SIZE; + Aqa.Rsvd1 = 0; + Aqa.Acqs = NVME_ACQ_SIZE; + Aqa.Rsvd2 = 0; + + /// + /// Address of admin submission queue. + /// + Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Nvme) & ~0xFFF); + + /// + /// Address of admin completion queue. + /// + Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Nvme) & ~0xFFF); + + /// + /// Address of I/O submission & completion queue. + /// + Nvme->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Nvme); // NVME_ADMIN_QUEUE + Nvme->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Nvme); // NVME_ADMIN_QUEUE + Nvme->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Nvme, 0); // NVME_IO_QUEUE + Nvme->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Nvme, 0); // NVME_IO_QUEUE + + DEBUG ((DEBUG_INFO, "Admin Submission Queue Size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs)); + DEBUG ((DEBUG_INFO, "Admin Completion Queue Size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs)); + DEBUG ((DEBUG_INFO, "Admin Submission Queue (SqBuffer[0]) = [%08X]\n", Nvme->SqBuffer[0])); + DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) = [%08X]\n", Nvme->CqBuffer[0])); + DEBUG ((DEBUG_INFO, "I/O Submission Queue (SqBuffer[1]) = [%08X]\n", Nvme->SqBuffer[1])); + DEBUG ((DEBUG_INFO, "I/O Completion Queue (CqBuffer[1]) = [%08X]\n", Nvme->CqBuffer[1])); + + /// + /// Program admin queue attributes. + /// + Status = NVME_SET_AQA (Nvme, &Aqa); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Program admin submission queue address. + /// + Status = NVME_SET_ASQ (Nvme, &Asq); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Program admin completion queue address. + /// + Status = NVME_SET_ACQ (Nvme, &Acq); + if (EFI_ERROR(Status)) { + goto Done; + } + + Status = NvmeEnableController (Nvme); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Create one I/O completion queue. + /// + Status = NvmeCreateIoCompletionQueue (Nvme); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Create one I/O Submission queue. + /// + Status = NvmeCreateIoSubmissionQueue (Nvme); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Get current Identify Controller Data + /// + Nvme->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)(UINTN) NVME_CONTROL_DATA_BASE (Nvme); + Status = NvmeIdentifyController (Nvme, Nvme->ControllerData); + if (EFI_ERROR(Status)) { + goto Done; + } + + /// + /// Dump NvmExpress Identify Controller Data + /// + Nvme->ControllerData->Sn[19] = 0; + Nvme->ControllerData->Mn[39] = 0; + //NvmeDumpIdentifyController (Nvme->ControllerData); + + /// + /// Get current Identify Namespace Data + /// + Nvme->NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)NVME_NAMESPACE_DATA_BASE (Nvme); + Status = NvmeIdentifyNamespace (Nvme, Nvme->Nsid, Nvme->NamespaceData); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeIdentifyNamespace fail, Status = %r\n", Status)); + goto Done; + } + + /// + /// Dump NvmExpress Identify Namespace Data + /// + if (Nvme->NamespaceData->Ncap == 0) { + DEBUG ((DEBUG_ERROR, "Invalid Namespace, Ncap: %lx\n", Nvme->NamespaceData->Ncap)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + Nvme->BlockSize = NvmeGetBlockSize (Nvme); + Nvme->LastBlock = NvmeGetLastLba (Nvme); + + Nvme->State = NvmeStatusInit; + + return EFI_SUCCESS; + +Done: + return Status; +} + +/** + Un-initialize the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - The NVM Express Controller is un-initialized successfully. + @retval Others - A device error occurred while un-initializing the controller. + +**/ +EFI_STATUS +NvmeControllerExit ( + IN NVME_CONTEXT *Nvme + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + if (Nvme->State == NvmeStatusInit || Nvme->State == NvmeStatusMax) { + /// + /// Destroy I/O Submission queue. + /// + Status = NvmeDestroyIoSubmissionQueue (Nvme); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeDestroyIoSubmissionQueue fail, Status = %r\n", Status)); + return Status; + } + + /// + /// Destroy I/O completion queue. + /// + Status = NvmeDestroyIoCompletionQueue (Nvme); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeDestroyIoCompletionQueue fail, Status = %r\n", Status)); + return Status; + } + + Status = NvmeShutdownController (Nvme); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeShutdownController fail, Status: %r\n", Status)); + } + } + + /// + /// Disable PCIE decode + /// + PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x0); + PciWrite32 (Nvme->PciBase + 0x10, 0); // MLBAR (BAR0) + PciWrite32 (Nvme->PciBase + 0x14, 0); // MUBAR (BAR1) + + Nvme->State = NvmeStatusUnknown; + return Status; +} + +/** + Read sector Data from the NVMe device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in,out] Buffer - The Buffer used to store the Data read from the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be read. + + @retval EFI_SUCCESS - Datum are read from the device. + @retval Others - Fail to read all the datum. + +**/ +EFI_STATUS +NvmeReadSectors ( + IN NVME_CONTEXT *Nvme, + IN OUT UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks + ) +{ + UINT32 Bytes; + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + UINT32 BlockSize; + + BlockSize = Nvme->BlockSize; + Bytes = Blocks * BlockSize; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC; + CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++; + CommandPacket.NvmeCmd->Nsid = Nvme->Nsid; + CommandPacket.TransferBuffer = Buffer; + + CommandPacket.TransferLength = Bytes; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_IO_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba; + CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32)); + CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF; + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID; + + Status = NvmePassThru ( + Nvme, + Nvme->Nsid, + 0, + &CommandPacket + ); + + return Status; +} + +/** + Write sector Data to the NVMe device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Buffer - The Buffer to be written into the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be written. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeWriteSectors ( + IN NVME_CONTEXT *Nvme, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + UINT32 Bytes; + UINT32 BlockSize; + + BlockSize = Nvme->BlockSize; + Bytes = Blocks * BlockSize; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC; + CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++; + CommandPacket.NvmeCmd->Nsid = Nvme->Nsid; + CommandPacket.TransferBuffer = Buffer; + + CommandPacket.TransferLength = Bytes; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_IO_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba; + CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32)); + CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF; + + CommandPacket.MetadataBuffer = (UINT64)(UINTN)NULL; + CommandPacket.MetadataLength = 0; + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID; + + Status = NvmePassThru ( + Nvme, + Nvme->Nsid, + 0, + &CommandPacket + ); + + return Status; +} + +/** + Flushes all modified Data to the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeFlush ( + IN NVME_CONTEXT *Nvme + ) +{ + NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + NVM_EXPRESS_COMMAND Command; + NVM_EXPRESS_RESPONSE Response; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND)); + ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeResponse = &Response; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC; + CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++; + CommandPacket.NvmeCmd->Nsid = Nvme->Nsid; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueId = NVME_IO_QUEUE; + + Status = NvmePassThru ( + Nvme, + Nvme->Nsid, + 0, + &CommandPacket + ); + if (!EFI_ERROR (Status)) { + Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId); + } + + return Status; +} + +/** + Read some blocks from the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[out] Buffer - The Buffer used to store the Data read from the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be read. + + @retval EFI_SUCCESS - Datum are read from the device. + @retval Others - Fail to read all the datum. + +**/ +EFI_STATUS +NvmeRead ( + IN NVME_CONTEXT *Nvme, + OUT UINT64 Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UINT32 MaxTransferBlocks; + + ASSERT (Blocks <= NVME_MAX_SECTORS); + Status = EFI_SUCCESS; + BlockSize = Nvme->BlockSize; + if (Nvme->ControllerData->Mdts != 0) { + MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize; + } else { + MaxTransferBlocks = 1024; + } + + while (Blocks > 0) { + if (Blocks > MaxTransferBlocks) { + Status = NvmeReadSectors (Nvme, Buffer, Lba, MaxTransferBlocks); + + Blocks -= MaxTransferBlocks; + Buffer += (MaxTransferBlocks * BlockSize); + Lba += MaxTransferBlocks; + } else { + Status = NvmeReadSectors (Nvme, Buffer, Lba, (UINT32) Blocks); + Blocks = 0; + } + + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeRead fail, Status = %r\n", Status)); + break; + } + } + + return Status; +} + +/** + Write some blocks to the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Buffer - The Buffer to be written into the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be written. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeWrite ( + IN NVME_CONTEXT *Nvme, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UINT32 MaxTransferBlocks; + + ASSERT (Blocks <= NVME_MAX_SECTORS); + Status = EFI_SUCCESS; + BlockSize = Nvme->BlockSize; + + if (Nvme->ControllerData->Mdts != 0) { + MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize; + } else { + MaxTransferBlocks = 1024; + } + + while (Blocks > 0) { + if (Blocks > MaxTransferBlocks) { + Status = NvmeWriteSectors (Nvme, Buffer, Lba, MaxTransferBlocks); + + Blocks -= MaxTransferBlocks; + Buffer += (MaxTransferBlocks * BlockSize); + Lba += MaxTransferBlocks; + } else { + Status = NvmeWriteSectors (Nvme, Buffer, Lba, (UINT32) Blocks); + Blocks = 0; + } + + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "NvmeWrite fail, Status = %r\n", Status)); + break; + } + } + + return Status; +} diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.h new file mode 100644 index 0000000000..3fef3dbc1c --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeMode.h @@ -0,0 +1,455 @@ +/** @file + Header file for NVMe function definitions + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __OPAL_PASSWORD_NVME_MODE_H__ +#define __OPAL_PASSWORD_NVME_MODE_H__ + + +#include "OpalNvmeReg.h" + +#define NVME_MAX_SECTORS 0x10000 +// +// QueueId +// +#define NVME_ADMIN_QUEUE 0x00 +#define NVME_IO_QUEUE 0x01 + +typedef struct { + UINT8 Opcode; + UINT8 FusedOperation; + #define NORMAL_CMD 0x00 + #define FUSED_FIRST_CMD 0x01 + #define FUSED_SECOND_CMD 0x02 + UINT16 Cid; +} NVME_CDW0; + +typedef struct { + NVME_CDW0 Cdw0; + UINT8 Flags; + #define CDW10_VALID 0x01 + #define CDW11_VALID 0x02 + #define CDW12_VALID 0x04 + #define CDW13_VALID 0x08 + #define CDW14_VALID 0x10 + #define CDW15_VALID 0x20 + UINT32 Nsid; + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} NVM_EXPRESS_COMMAND; + +typedef struct { + UINT32 Cdw0; + UINT32 Cdw1; + UINT32 Cdw2; + UINT32 Cdw3; +} NVM_EXPRESS_RESPONSE; + +typedef struct { + UINT64 CommandTimeout; + UINT64 TransferBuffer; + UINT32 TransferLength; + UINT64 MetadataBuffer; + UINT32 MetadataLength; + UINT8 QueueId; + NVM_EXPRESS_COMMAND *NvmeCmd; + NVM_EXPRESS_RESPONSE *NvmeResponse; +} NVM_EXPRESS_PASS_THRU_COMMAND_PACKET; + + +#pragma pack(1) + +// Internal fields +typedef enum { + NvmeStatusUnknown, + NvmeStatusInit, + NvmeStatusInuse, + NvmeStatusMax, +} NVME_STATUS; + +typedef struct { + UINT32 Nbar; + VOID *BaseMem; + VOID *BaseMemMapping; + BOOLEAN PollCancellation; + UINT16 NvmeInitWaitTime; + + NVME_STATUS State; + UINT8 BusID; + UINT8 DeviceID; + UINT8 FuncID; + UINTN PciBase; + + UINT32 Nsid; + UINT64 Nsuuid; + UINT32 BlockSize; + EFI_LBA LastBlock; + + // + // Pointers to 4kB aligned submission & completion queues. + // + NVME_SQ *SqBuffer[NVME_MAX_IO_QUEUES]; + NVME_CQ *CqBuffer[NVME_MAX_IO_QUEUES]; + UINT16 Cid[NVME_MAX_IO_QUEUES]; + + // + // Submission and completion queue indices. + // + NVME_SQTDBL SqTdbl[NVME_MAX_IO_QUEUES]; + NVME_CQHDBL CqHdbl[NVME_MAX_IO_QUEUES]; + UINT8 Pt[NVME_MAX_IO_QUEUES]; + + UINTN SqeCount[NVME_MAX_IO_QUEUES]; + + // + // Nvme controller capabilities + // + NVME_CAP Cap; + + // + // pointer to identify controller Data + // + NVME_ADMIN_CONTROLLER_DATA *ControllerData; + NVME_ADMIN_NAMESPACE_DATA *NamespaceData; +} NVME_CONTEXT; + +#pragma pack() + +/** + Transfer MMIO Data to memory. + + @param[in,out] MemBuffer - Destination: Memory address + @param[in] MmioAddr - Source: MMIO address + @param[in] Size - Size for read + + @retval EFI_SUCCESS - MMIO read sucessfully +**/ +EFI_STATUS +NvmeMmioRead ( + IN OUT VOID *MemBuffer, + IN UINTN MmioAddr, + IN UINTN Size + ); + +/** + Transfer memory Data to MMIO. + + @param[in,out] MmioAddr - Destination: MMIO address + @param[in] MemBuffer - Source: Memory address + @param[in] Size - Size for write + + @retval EFI_SUCCESS - MMIO write sucessfully +**/ +EFI_STATUS +NvmeMmioWrite ( + IN OUT UINTN MmioAddr, + IN VOID *MemBuffer, + IN UINTN Size + ); + +/** + Transfer memory data to MMIO. + + @param[in,out] MmioAddr - Destination: MMIO address + @param[in] MemBuffer - Source: Memory address + @param[in] Size - Size for write + + @retval EFI_SUCCESS - MMIO write sucessfully +**/ +EFI_STATUS +OpalPciWrite ( + IN OUT UINTN MmioAddr, + IN VOID *MemBuffer, + IN UINTN Size + ); + +/** + Transfer MMIO data to memory. + + @param[in,out] MemBuffer - Destination: Memory address + @param[in] MmioAddr - Source: MMIO address + @param[in] Size - Size for read + + @retval EFI_SUCCESS - MMIO read sucessfully +**/ +EFI_STATUS +OpalPciRead ( + IN OUT VOID *MemBuffer, + IN UINTN MmioAddr, + IN UINTN Size + ); + +/** + Allocate transfer-related Data struct which is used at Nvme. + + @param[in, out] Nvme The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_OUT_OF_RESOURCE No enough resource. + @retval EFI_SUCCESS Successful to allocate resource. + +**/ +EFI_STATUS +EFIAPI +NvmeAllocateResource ( + IN OUT NVME_CONTEXT *Nvme + ); + +/** + Free allocated transfer-related Data struct which is used at NVMe. + + @param[in, out] Nvme The pointer to the NVME_CONTEXT Data structure. + +**/ +VOID +EFIAPI +NvmeFreeResource ( + IN OUT NVME_CONTEXT *Nvme + ); + +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking + I/O functionality is optional. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] NamespaceId - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent. + A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace + ID specifies that the command packet should be sent to all valid namespaces. + @param[in] NamespaceUuid - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent. + A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace + UUID specifies that the command packet should be sent to all valid namespaces. + @param[in,out] Packet - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified + by NamespaceId. + + @retval EFI_SUCCESS - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_NOT_READY - The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR - A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED - The command described by the NVM Express Command Packet is not supported by the host adapter. + The NVM Express Command Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT - A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +EFI_STATUS +NvmePassThru ( + IN NVME_CONTEXT *Nvme, + IN UINT32 NamespaceId, + IN UINT64 NamespaceUuid, + IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet + ); + +/** + Waits until all NVME commands completed. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - All NVME commands have completed + @retval EFI_TIMEOUT - Timeout occured + @retval EFI_NOT_READY - Not all NVME commands have completed + @retval others - Error occurred on device side. +**/ +EFI_STATUS +NvmeWaitAllComplete ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ); + +/** + Initialize the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - The NVM Express Controller is initialized successfully. + @retval Others - A device error occurred while initializing the controller. + +**/ +EFI_STATUS +NvmeControllerInit ( + IN NVME_CONTEXT *Nvme + ); + +/** + Un-initialize the Nvm Express controller. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - The NVM Express Controller is un-initialized successfully. + @retval Others - A device error occurred while un-initializing the controller. + +**/ +EFI_STATUS +NvmeControllerExit ( + IN NVME_CONTEXT *Nvme + ); + +/** + Check whether there are available command slots. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - Available command slot is found + @retval EFI_NOT_READY - No available command slot is found + @retval EFI_DEVICE_ERROR - Error occurred on device side. + +**/ +EFI_STATUS +NvmeHasFreeCmdSlot ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ); + +/** + Check whether all command slots are clean. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Qid - Queue index + + @retval EFI_SUCCESS - All command slots are clean + @retval EFI_NOT_READY - Not all command slots are clean + @retval EFI_DEVICE_ERROR - Error occurred on device side. + +**/ +EFI_STATUS +NvmeIsAllCmdSlotClean ( + IN NVME_CONTEXT *Nvme, + IN UINT8 Qid + ); + +/** + Read sector Data from the NVMe device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in,out] Buffer - The Buffer used to store the Data read from the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be read. + + @retval EFI_SUCCESS - Datum are read from the device. + @retval Others - Fail to read all the datum. + +**/ +EFI_STATUS +NvmeReadSectors ( + IN NVME_CONTEXT *Nvme, + IN OUT UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks + ); + +/** + Write sector Data to the NVMe device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Buffer - The Buffer to be written into the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be written. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeWriteSectors ( + IN NVME_CONTEXT *Nvme, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINT32 Blocks + ); + +/** + Flushes all modified Data to the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeFlush ( + IN NVME_CONTEXT *Nvme + ); + +/** + Read some blocks from the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[out] Buffer - The Buffer used to store the Data read from the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be read. + + @retval EFI_SUCCESS - Datum are read from the device. + @retval Others - Fail to read all the datum. + +**/ +EFI_STATUS +NvmeRead ( + IN NVME_CONTEXT *Nvme, + OUT UINT64 Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ); + +/** + Write some blocks to the device. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] Buffer - The Buffer to be written into the device. + @param[in] Lba - The start block number. + @param[in] Blocks - Total block number to be written. + + @retval EFI_SUCCESS - Datum are written into the Buffer. + @retval Others - Fail to write all the datum. + +**/ +EFI_STATUS +NvmeWrite ( + IN NVME_CONTEXT *Nvme, + IN UINT64 Buffer, + IN UINT64 Lba, + IN UINTN Blocks + ); + +/** + Security send and receive commands. + + @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure. + @param[in] SendCommand - The flag to indicate the command type, TRUE for Send command and FALSE for receive command + @param[in] SecurityProtocol - Security Protocol + @param[in] SpSpecific - Security Protocol Specific + @param[in] TransferLength - Transfer Length of Buffer (in bytes) - always a multiple of 512 + @param[in,out] TransferBuffer - Address of Data to transfer + + @return EFI_SUCCESS - Successfully create io submission queue. + @return others - Fail to send/receive commands. + +**/ +EFI_STATUS +NvmeSecuritySendReceive ( + IN NVME_CONTEXT *Nvme, + IN BOOLEAN SendCommand, + IN UINT8 SecurityProtocol, + IN UINT16 SpSpecific, + IN UINTN TransferLength, + IN OUT VOID *TransferBuffer + ); + +#endif diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeReg.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeReg.h new file mode 100644 index 0000000000..03376b9e6c --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalNvmeReg.h @@ -0,0 +1,815 @@ +/** @file + Header file for Registers and Structure definitions + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ +#ifndef __OPAL_PASSWORD_NVME_REG_H__ +#define __OPAL_PASSWORD_NVME_REG_H__ + +// +// PCI Header for PCIe root port configuration +// +#define NVME_PCIE_PCICMD 0x04 +#define NVME_PCIE_BNUM 0x18 +#define NVME_PCIE_SEC_BNUM 0x19 +#define NVME_PCIE_IOBL 0x1C +#define NVME_PCIE_MBL 0x20 +#define NVME_PCIE_PMBL 0x24 +#define NVME_PCIE_PMBU32 0x28 +#define NVME_PCIE_PMLU32 0x2C +#define NVME_PCIE_INTR 0x3C + +// +// NVMe related definitions +// +#define PCI_CLASS_MASS_STORAGE_NVM 0x08 // mass storage sub-class non-volatile memory. +#define PCI_IF_NVMHCI 0x02 // mass storage programming interface NVMHCI. + +#define NVME_ASQ_SIZE 1 // Number of admin submission queue entries, which is 0-based +#define NVME_ACQ_SIZE 1 // Number of admin completion queue entries, which is 0-based + +#define NVME_CSQ_SIZE 63 // Number of I/O submission queue entries, which is 0-based +#define NVME_CCQ_SIZE 63 // Number of I/O completion queue entries, which is 0-based + +#define NVME_MAX_IO_QUEUES 2 // Number of I/O queues supported by the driver, 1 for AQ, 1 for CQ + +#define NVME_CSQ_DEPTH (NVME_CSQ_SIZE+1) +#define NVME_CCQ_DEPTH (NVME_CCQ_SIZE+1) +#define NVME_PRP_SIZE (4) // Pages of PRP list + +#define NVME_CONTROLLER_ID 0 + +// +// Time out Value for Nvme transaction execution +// +#define NVME_GENERIC_TIMEOUT 5000000 ///< us +#define NVME_CMD_WAIT 100 ///< us +#define NVME_CMD_TIMEOUT 20000000 ///< us + + + +#define NVME_MEM_MAX_SIZE \ + (( \ + 1 /* Controller Data */ + \ + 1 /* Identify Data */ + \ + 1 /* ASQ */ + \ + 1 /* ACQ */ + \ + 1 /* SQs */ + \ + 1 /* CQs */ + \ + NVME_PRP_SIZE * NVME_CSQ_DEPTH /* PRPs */ + \ + 1 /* SECURITY */ \ + ) * EFI_PAGE_SIZE) + + +// +// controller register offsets +// +#define NVME_CAP_OFFSET 0x0000 // Controller Capabilities +#define NVME_VER_OFFSET 0x0008 // Version +#define NVME_INTMS_OFFSET 0x000c // Interrupt Mask Set +#define NVME_INTMC_OFFSET 0x0010 // Interrupt Mask Clear +#define NVME_CC_OFFSET 0x0014 // Controller Configuration +#define NVME_CSTS_OFFSET 0x001c // Controller Status +#define NVME_AQA_OFFSET 0x0024 // Admin Queue Attributes +#define NVME_ASQ_OFFSET 0x0028 // Admin Submission Queue Base Address +#define NVME_ACQ_OFFSET 0x0030 // Admin Completion Queue Base Address +#define NVME_SQ0_OFFSET 0x1000 // Submission Queue 0 (admin) Tail Doorbell +#define NVME_CQ0_OFFSET 0x1004 // Completion Queue 0 (admin) Head Doorbell + +// +// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD)) +// Get the doorbell stride bit shift Value from the controller capabilities. +// +#define NVME_SQTDBL_OFFSET(QID, DSTRD) 0x1000 + ((2 * (QID)) * (4 << (DSTRD))) // Submission Queue y (NVM) Tail Doorbell +#define NVME_CQHDBL_OFFSET(QID, DSTRD) 0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell + + +#pragma pack(1) + +// +// 3.1.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT16 Mqes; // Maximum Queue Entries Supported + UINT8 Cqr:1; // Contiguous Queues Required + UINT8 Ams:2; // Arbitration Mechanism Supported + UINT8 Rsvd1:5; + UINT8 To; // Timeout + UINT16 Dstrd:4; + UINT16 Rsvd2:1; + UINT16 Css:4; // Command Sets Supported + UINT16 Rsvd3:7; + UINT8 Mpsmin:4; + UINT8 Mpsmax:4; + UINT8 Rsvd4; +} NVME_CAP; + +// +// 3.1.2 Offset 08h: VS - Version +// +typedef struct { + UINT16 Mnr; // Minor version number + UINT16 Mjr; // Major version number +} NVME_VER; + +// +// 3.1.5 Offset 14h: CC - Controller Configuration +// +typedef struct { + UINT16 En:1; // Enable + UINT16 Rsvd1:3; + UINT16 Css:3; // Command Set Selected + UINT16 Mps:4; // Memory Page Size + UINT16 Ams:3; // Arbitration Mechanism Selected + UINT16 Shn:2; // Shutdown Notification + UINT8 Iosqes:4; // I/O Submission Queue Entry Size + UINT8 Iocqes:4; // I/O Completion Queue Entry Size + UINT8 Rsvd2; +} NVME_CC; + +// +// 3.1.6 Offset 1Ch: CSTS - Controller Status +// +typedef struct { + UINT32 Rdy:1; // Ready + UINT32 Cfs:1; // Controller Fatal Status + UINT32 Shst:2; // Shutdown Status + UINT32 Nssro:1; // NVM Subsystem Reset Occurred + UINT32 Rsvd1:27; +} NVME_CSTS; + +// +// 3.1.8 Offset 24h: AQA - Admin Queue Attributes +// +typedef struct { + UINT16 Asqs:12; // Submission Queue Size + UINT16 Rsvd1:4; + UINT16 Acqs:12; // Completion Queue Size + UINT16 Rsvd2:4; +} NVME_AQA; + +// +// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address +// +#define NVME_ASQ UINT64 + +// +// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address +// +#define NVME_ACQ UINT64 + +// +// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell +// +typedef struct { + UINT16 Sqt; + UINT16 Rsvd1; +} NVME_SQTDBL; + +// +// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell +// +typedef struct { + UINT16 Cqh; + UINT16 Rsvd1; +} NVME_CQHDBL; + +// +// NVM command set structures +// +// Read Command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_READ; + +// +// Write Command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE; + +// +// Flush +// +typedef struct { + // + // CDW 10 + // + UINT32 Flush; /* Flush */ +} NVME_FLUSH; + +// +// Write Uncorrectable command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT32 Nlb:16; /* Number of Logical Blocks */ + UINT32 Rsvd1:16; +} NVME_WRITE_UNCORRECTABLE; + +// +// Write Zeroes command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE_ZEROES; + +// +// Compare command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_COMPARE; + +typedef union { + NVME_READ Read; + NVME_WRITE Write; + NVME_FLUSH Flush; + NVME_WRITE_UNCORRECTABLE WriteUncorrectable; + NVME_WRITE_ZEROES WriteZeros; + NVME_COMPARE Compare; +} NVME_CMD; + +typedef struct { + UINT16 Mp; /* Maximum Power */ + UINT8 Rsvd1; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Mps:1; /* Max Power Scale */ + UINT8 Nops:1; /* Non-Operational State */ + UINT8 Rsvd2:6; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Enlat; /* Entry Latency */ + UINT32 Exlat; /* Exit Latency */ + UINT8 Rrt:5; /* Relative Read Throughput */ + UINT8 Rsvd3:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rrl:5; /* Relative Read Leatency */ + UINT8 Rsvd4:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwt:5; /* Relative Write Throughput */ + UINT8 Rsvd5:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwl:5; /* Relative Write Leatency */ + UINT8 Rsvd6:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rsvd7[16]; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_PSDESCRIPTOR; + +// +// Identify Controller Data +// +typedef struct { + // + // Controller Capabilities and Features 0-255 + // + UINT16 Vid; /* PCI Vendor ID */ + UINT16 Ssvid; /* PCI sub-system vendor ID */ + UINT8 Sn[20]; /* Produce serial number */ + + UINT8 Mn[40]; /* Proeduct model number */ + UINT8 Fr[8]; /* Firmware Revision */ + UINT8 Rab; /* Recommended Arbitration Burst */ + UINT8 Ieee_oiu[3]; /* Organization Unique Identifier */ + UINT8 Cmic; /* Multi-interface Capabilities */ + UINT8 Mdts; /* Maximum Data Transfer Size */ + UINT8 Cntlid[2]; /* Controller ID */ + UINT8 Rsvd1[176]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Admin Command Set Attributes + // + UINT16 Oacs; /* Optional Admin Command Support */ + UINT8 Acl; /* Abort Command Limit */ + UINT8 Aerl; /* Async Event Request Limit */ + UINT8 Frmw; /* Firmware updates */ + UINT8 Lpa; /* Log Page Attributes */ + UINT8 Elpe; /* Error Log Page Entries */ + UINT8 Npss; /* Number of Power States Support */ + UINT8 Avscc; /* Admin Vendor Specific Command Configuration */ + UINT8 Apsta; /* Autonomous Power State Transition Attributes */ + UINT8 Rsvd2[246]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // NVM Command Set Attributes + // + UINT8 Sqes; /* Submission Queue Entry Size */ + UINT8 Cqes; /* Completion Queue Entry Size */ + UINT16 Rsvd3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Nn; /* Number of Namespaces */ + UINT16 Oncs; /* Optional NVM Command Support */ + UINT16 Fuses; /* Fused Operation Support */ + UINT8 Fna; /* Format NVM Attributes */ + UINT8 Vwc; /* Volatile Write Cache */ + UINT16 Awun; /* Atomic Write Unit Normal */ + UINT16 Awupf; /* Atomic Write Unit Power Fail */ + UINT8 Nvscc; /* NVM Vendor Specific Command Configuration */ + UINT8 Rsvd4; /* Reserved as of Nvm Express 1.1 Spec */ + UINT16 Acwu; /* Atomic Compare & Write Unit */ + UINT16 Rsvd5; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Sgls; /* SGL Support */ + UINT8 Rsvd6[164]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // I/O Command set Attributes + // + UINT8 Rsvd7[1344]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Power State Descriptors + // + NVME_PSDESCRIPTOR PsDescriptor[32]; + + UINT8 VendorData[1024]; /* Vendor specific Data */ +} NVME_ADMIN_CONTROLLER_DATA; + +typedef struct { + UINT16 Security : 1; /* supports security send/receive commands */ + UINT16 Format : 1; /* supports format nvm command */ + UINT16 Firmware : 1; /* supports firmware activate/download commands */ + UINT16 Oacs_rsvd : 13; + } OACS; // optional admin command support: NVME_ADMIN_CONTROLLER_DATA.Oacs + +typedef struct { + UINT16 Ms; /* Metadata Size */ + UINT8 Lbads; /* LBA Data Size */ + UINT8 Rp:2; /* Relative Performance */ + #define LBAF_RP_BEST 00b + #define LBAF_RP_BETTER 01b + #define LBAF_RP_GOOD 10b + #define LBAF_RP_DEGRADED 11b + UINT8 Rsvd1:6; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_LBAFORMAT; + +// +// Identify Namespace Data +// +typedef struct { + // + // NVM Command Set Specific + // + UINT64 Nsze; /* Namespace Size (total number of blocks in formatted namespace) */ + UINT64 Ncap; /* Namespace Capacity (max number of logical blocks) */ + UINT64 Nuse; /* Namespace Utilization */ + UINT8 Nsfeat; /* Namespace Features */ + UINT8 Nlbaf; /* Number of LBA Formats */ + UINT8 Flbas; /* Formatted LBA Size */ + UINT8 Mc; /* Metadata Capabilities */ + UINT8 Dpc; /* End-to-end Data Protection capabilities */ + UINT8 Dps; /* End-to-end Data Protection Type Settings */ + UINT8 Nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */ + UINT8 Rescap; /* Reservation Capabilities */ + UINT8 Rsvd1[88]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT64 Eui64; /* IEEE Extended Unique Identifier */ + // + // LBA Format + // + NVME_LBAFORMAT LbaFormat[16]; + + UINT8 Rsvd2[192]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 VendorData[3712]; /* Vendor specific Data */ +} NVME_ADMIN_NAMESPACE_DATA; + +// +// NvmExpress Admin Identify Cmd +// +typedef struct { + // + // CDW 10 + // + UINT32 Cns:2; + UINT32 Rsvd1:30; +} NVME_ADMIN_IDENTIFY; + +// +// NvmExpress Admin Create I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Ien:1; /* Interrupts Enabled */ + UINT32 Rsvd1:14; /* reserved as of Nvm Express 1.1 Spec */ + UINT32 Iv:16; /* Interrupt Vector */ +} NVME_ADMIN_CRIOCQ; + +// +// NvmExpress Admin Create I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Qprio:2; /* Queue Priority */ + UINT32 Rsvd1:13; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Cqid:16; /* Completion Queue ID */ +} NVME_ADMIN_CRIOSQ; + +// +// NvmExpress Admin Delete I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOCQ; + +// +// NvmExpress Admin Delete I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOSQ; + +// +// NvmExpress Admin Security Send +// +typedef struct { + // + // CDW 10 + // + UINT32 Resv:8; /* Reserve */ + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + + // + // CDW 11 + // + UINT32 Tl; /* Transfer Length */ +} NVME_ADMIN_SECSEND; + +// +// NvmExpress Admin Abort Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Sqid:16; /* Submission Queue identifier */ + UINT32 Cid:16; /* Command Identifier */ +} NVME_ADMIN_ABORT; + +// +// NvmExpress Admin Firmware Activate Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fs:3; /* Submission Queue identifier */ + UINT32 Aa:2; /* Command Identifier */ + UINT32 Rsvd1:27; +} NVME_ADMIN_FIRMWARE_ACTIVATE; + +// +// NvmExpress Admin Firmware Image Download Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Numd; /* Number of Dwords */ + // + // CDW 11 + // + UINT32 Ofst; /* Offset */ +} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD; + +// +// NvmExpress Admin Get Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Sel:3; /* Select */ + UINT32 Rsvd1:21; +} NVME_ADMIN_GET_FEATURES; + +// +// NvmExpress Admin Get Log Page Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lid:8; /* Log Page Identifier */ + #define LID_ERROR_INFO + #define LID_SMART_INFO + #define LID_FW_SLOT_INFO + UINT32 Rsvd1:8; + UINT32 Numd:12; /* Number of Dwords */ + UINT32 Rsvd2:4; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_ADMIN_GET_LOG_PAGE; + +// +// NvmExpress Admin Set Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Rsvd1:23; + UINT32 Sv:1; /* Save */ +} NVME_ADMIN_SET_FEATURES; + +// +// NvmExpress Admin Format NVM Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lbaf:4; /* LBA Format */ + UINT32 Ms:1; /* Metadata Settings */ + UINT32 Pi:3; /* Protection Information */ + UINT32 Pil:1; /* Protection Information Location */ + UINT32 Ses:3; /* Secure Erase Settings */ + UINT32 Rsvd1:20; +} NVME_ADMIN_FORMAT_NVM; + +// +// NvmExpress Admin Security Receive Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Al; /* Allocation Length */ +} NVME_ADMIN_SECURITY_RECEIVE; + +// +// NvmExpress Admin Security Send Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Tl; /* Transfer Length */ +} NVME_ADMIN_SECURITY_SEND; + +typedef union { + NVME_ADMIN_IDENTIFY Identify; + NVME_ADMIN_CRIOCQ CrIoCq; + NVME_ADMIN_CRIOSQ CrIoSq; + NVME_ADMIN_DEIOCQ DeIoCq; + NVME_ADMIN_DEIOSQ DeIoSq; + NVME_ADMIN_ABORT Abort; + NVME_ADMIN_FIRMWARE_ACTIVATE Activate; + NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD FirmwareImageDownload; + NVME_ADMIN_GET_FEATURES GetFeatures; + NVME_ADMIN_GET_LOG_PAGE GetLogPage; + NVME_ADMIN_SET_FEATURES SetFeatures; + NVME_ADMIN_FORMAT_NVM FormatNvm; + NVME_ADMIN_SECURITY_RECEIVE SecurityReceive; + NVME_ADMIN_SECURITY_SEND SecuritySend; +} NVME_ADMIN_CMD; + +typedef struct { + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} NVME_RAW; + +typedef union { + NVME_ADMIN_CMD Admin; // Union of Admin commands + NVME_CMD Nvm; // Union of Nvm commands + NVME_RAW Raw; +} NVME_PAYLOAD; + +// +// Submission Queue +// +typedef struct { + // + // CDW 0, Common to all comnmands + // + UINT8 Opc; // Opcode + UINT8 Fuse:2; // Fused Operation + UINT8 Rsvd1:5; + UINT8 Psdt:1; // PRP or SGL for Data Transfer + UINT16 Cid; // Command Identifier + + // + // CDW 1 + // + UINT32 Nsid; // Namespace Identifier + + // + // CDW 2,3 + // + UINT64 Rsvd2; + + // + // CDW 4,5 + // + UINT64 Mptr; // Metadata Pointer + + // + // CDW 6-9 + // + UINT64 Prp[2]; // First and second PRP entries + + NVME_PAYLOAD Payload; + +} NVME_SQ; + +// +// Completion Queue +// +typedef struct { + // + // CDW 0 + // + UINT32 Dword0; + // + // CDW 1 + // + UINT32 Rsvd1; + // + // CDW 2 + // + UINT16 Sqhd; // Submission Queue Head Pointer + UINT16 Sqid; // Submission Queue Identifier + // + // CDW 3 + // + UINT16 Cid; // Command Identifier + UINT16 Pt:1; // Phase Tag + UINT16 Sc:8; // Status Code + UINT16 Sct:3; // Status Code Type + UINT16 Rsvd2:2; + UINT16 Mo:1; // More + UINT16 Dnr:1; // Retry +} NVME_CQ; + +// +// Nvm Express Admin cmd opcodes +// +#define NVME_ADMIN_DELIOSQ_OPC 0 +#define NVME_ADMIN_CRIOSQ_OPC 1 +#define NVME_ADMIN_DELIOCQ_OPC 4 +#define NVME_ADMIN_CRIOCQ_OPC 5 +#define NVME_ADMIN_IDENTIFY_OPC 6 +#define NVME_ADMIN_SECURITY_SEND_OPC 0x81 +#define NVME_ADMIN_SECURITY_RECV_OPC 0x82 + +#define NVME_IO_FLUSH_OPC 0 +#define NVME_IO_WRITE_OPC 1 +#define NVME_IO_READ_OPC 2 + +// +// Offset from the beginning of private Data queue Buffer +// +#define NVME_ASQ_BUF_OFFSET EFI_PAGE_SIZE + +#pragma pack() + +#endif + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordCommon.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordCommon.h new file mode 100644 index 0000000000..e10146e466 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordCommon.h @@ -0,0 +1,67 @@ +/** @file + Opal Password common header file. + +Copyright (c) 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _OPAL_PASSWORD_COMMON_H_ +#define _OPAL_PASSWORD_COMMON_H_ + +#define OPAL_MAX_PASSWORD_SIZE 32 + +#define OPAL_DEVICE_TYPE_UNKNOWN 0x0 +#define OPAL_DEVICE_TYPE_ATA 0x1 +#define OPAL_DEVICE_TYPE_NVME 0x2 + +typedef struct { + UINT16 Segment; + UINT8 Bus; + UINT8 Device; + UINT8 Function; + UINT8 Reserved; +} OPAL_PCI_DEVICE; + +typedef struct { + UINT16 Length; + OPAL_PCI_DEVICE Device; + UINT8 PasswordLength; + UINT8 Password[OPAL_MAX_PASSWORD_SIZE]; + UINT16 OpalBaseComId; + UINT32 BarAddr; +} OPAL_DEVICE_COMMON; + +#define OPAL_DEVICE_ATA_GUID { 0xcb934fe1, 0xb8cd, 0x46b1, { 0xa0, 0x58, 0xdd, 0xcb, 0x7, 0xb7, 0xb4, 0x17 } } + +typedef struct { + UINT16 Length; + OPAL_PCI_DEVICE Device; + UINT8 PasswordLength; + UINT8 Password[OPAL_MAX_PASSWORD_SIZE]; + UINT16 OpalBaseComId; + UINT32 BarAddr; + UINT16 Port; + UINT16 PortMultiplierPort; +} OPAL_DEVICE_ATA; + +#define OPAL_DEVICE_NVME_GUID { 0xde116925, 0xaf7f, 0x42d9, { 0x83, 0xc0, 0x7e, 0xd6, 0x26, 0x59, 0x0, 0xfb } } + +typedef struct { + UINT16 Length; + OPAL_PCI_DEVICE Device; + UINT8 PasswordLength; + UINT8 Password[OPAL_MAX_PASSWORD_SIZE]; + UINT16 OpalBaseComId; + UINT32 BarAddr; + UINT32 NvmeNamespaceId; + OPAL_PCI_DEVICE PciBridgeNode[0]; +} OPAL_DEVICE_NVME; + +#endif // _OPAL_PASSWORD_COMMON_H_ diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf new file mode 100644 index 0000000000..0ac550a728 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf @@ -0,0 +1,81 @@ +## @file +# This is a OpalPasswordDxe driver. +# +# This module is used to Management the Opal feature +# for Opal supported devices. +# +# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+# 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. +# +## +[Defines] + INF_VERSION = 0x00010007 + BASE_NAME = OpalPasswordDxe + FILE_GUID = E3E4048D-6C0C-43E4-AE1C-FFB579D8EF41 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = EfiDriverEntryPoint + UNLOAD_IMAGE = OpalEfiDriverUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + OpalDriver.c + OpalDriver.h + OpalPasswordCommon.h + OpalHii.c + OpalHii.h + OpalHiiCallbacks.c + OpalHiiFormValues.h + OpalHiiFormStrings.uni + OpalPasswordForm.vfr + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DxeServicesTableLib + UefiHiiServicesLib + BaseMemoryLib + DebugLib + HiiLib + PrintLib + DevicePathLib + UefiLib + TcgStorageOpalLib + Tcg2PhysicalPresenceLib + PciLib + S3BootScriptLib + LockBoxLib + +[Protocols] + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiStorageSecurityCommandProtocolGuid ## CONSUMES + gEfiComponentNameProtocolGuid ## PRODUCES + gEfiComponentName2ProtocolGuid ## PRODUCES + gEfiBlockIoProtocolGuid ## CONSUMES + gEfiPciIoProtocolGuid ## CONSUMES + gEfiDevicePathToTextProtocolGuid ## CONSUMES + +[Guids] + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + +[Depex] + gEfiHiiStringProtocolGuid AND gEfiHiiDatabaseProtocolGuid diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordForm.vfr b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordForm.vfr new file mode 100644 index 0000000000..d802ef305d --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordForm.vfr @@ -0,0 +1,309 @@ +/** @file + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalHiiFormValues.h" + + +#define EFI_HII_PLATFORM_SETUP_FORMSET_GUID \ + { 0x93039971, 0x8545, 0x4b04, { 0xb4, 0x5e, 0x32, 0xeb, 0x83, 0x26, 0x4, 0xe } } + +formset + guid = SETUP_FORMSET_GUID, + title = STRING_TOKEN(STR_OPAL), + help = STRING_TOKEN(STR_FORM_SET_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + // Define a Buffer Storage (EFI_IFR_VARSTORE) that will be filled + // out initially through extractConfig call + varstore OPAL_HII_CONFIGURATION, // This is the Data structure type + name = OpalHiiConfig, // Define referenced name in vfr + guid = SETUP_VARIABLE_GUID; // GUID of this Buffer storage + +form formid = FORMID_VALUE_MAIN_MENU, + title = STRING_TOKEN(STR_OPAL); + + //CONFIG_VARIABLE(HII_KEY(HII_KEY_ID_VAR_SUPPORTED_DISKS), SupportedDisks, 0x0, 0xFFFF); + suppressif TRUE; + numeric + name = SupportedDisks, + varid = OpalHiiConfig.SupportedDisks, + prompt = STRING_TOKEN(STR_NULL), + help = STRING_TOKEN(STR_NULL), + flags = INTERACTIVE, + key = 0x8002, + minimum = 0x0, + maximum = 0xFFFF, + endnumeric; + endif; + + subtitle text = STRING_TOKEN(STR_MAIN_OPAL_VERSION); + + subtitle text = STRING_TOKEN(STR_NULL); + + subtitle text = STRING_TOKEN(STR_MAIN_PHY_DISKS_LBL); + + //DISK( 0 ); + suppressif ( questionref(SupportedDisks) & ( 0x1 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_0 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8001; + endif; + + //DISK( 1 ); + suppressif ( questionref(SupportedDisks) & ( 0x2 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_1 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8101; + endif; + + //DISK( 2 ); + suppressif ( questionref(SupportedDisks) & ( 0x4 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_2 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8201; + endif; + + //DISK( 3 ); + suppressif ( questionref(SupportedDisks) & ( 0x8 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_3 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8301; + endif; + + //DISK( 4 ); + suppressif ( questionref(SupportedDisks) & ( 0x10 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_4 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8401; + endif; + + //DISK( 5 ); + suppressif ( questionref(SupportedDisks) & ( 0x20 ) ) == 0; + goto FORMID_VALUE_DISK_INFO_FORM_MAIN, + prompt = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_5 ), + help = STRING_TOKEN(STR_MAIN_GOTO_DISK_INFO_HELP), + flags = INTERACTIVE, + key = 0x8501; + endif; + + //No disks on system + suppressif ideqval OpalHiiConfig.NumDisks > 0; + text + help = STRING_TOKEN(STR_MAIN_NO_DISKS_PRESENT_LBL_HELP), + text = STRING_TOKEN(STR_MAIN_NO_DISKS_PRESENT_LBL); + endif; + + subtitle text = STRING_TOKEN(STR_NULL); + + grayoutif TRUE; + text + help = STRING_TOKEN(STR_BLOCKSID_STATUS_HELP), + text = STRING_TOKEN(STR_BLOCKSID_STATUS); + text + help = STRING_TOKEN(STR_BLOCKSID_STATUS_HELP), + text = STRING_TOKEN(STR_BLOCKSID_STATUS1); + text + help = STRING_TOKEN(STR_BLOCKSID_STATUS_HELP), + text = STRING_TOKEN(STR_BLOCKSID_STATUS2); + text + help = STRING_TOKEN(STR_BLOCKSID_STATUS_HELP), + text = STRING_TOKEN(STR_BLOCKSID_STATUS3); + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + oneof varid = OpalHiiConfig.EnableBlockSid, + questionid = 0x8004, + prompt = STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID), + help = STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID_HELP), + flags = INTERACTIVE, + option text = STRING_TOKEN(STR_NONE), value = 0, flags = DEFAULT | MANUFACTURING | RESET_REQUIRED; + option text = STRING_TOKEN(STR_ENABLED), value = 1, flags = RESET_REQUIRED; + option text = STRING_TOKEN(STR_DISABLED), value = 2, flags = RESET_REQUIRED; + option text = STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID_TRUE), value = 3, flags = RESET_REQUIRED; + option text = STRING_TOKEN(STR_DISK_INFO_ENABLE_BLOCKSID_FALSE), value = 4, flags = RESET_REQUIRED; + option text = STRING_TOKEN(STR_DISK_INFO_DISABLE_BLOCKSID_TRUE), value = 5, flags = RESET_REQUIRED; + option text = STRING_TOKEN(STR_DISK_INFO_DISABLE_BLOCKSID_FALSE), value = 6, flags = RESET_REQUIRED; + endoneof; + + + +endform; // MAIN MENU FORM + +// +///////////////// DISK INFO FORM ///////////////// +// +form formid = FORMID_VALUE_DISK_INFO_FORM_MAIN, + title = STRING_TOKEN(STR_OPAL); + + suppressif TRUE; + numeric + name = SelectedDiskAvailableActions, + varid = OpalHiiConfig.SelectedDiskAvailableActions, + prompt = STRING_TOKEN(STR_NULL), + help = STRING_TOKEN(STR_NULL), + flags = INTERACTIVE, + key = 0x8003, + minimum = 0x0, + maximum = 0xFFFF, + endnumeric; + endif; + + suppressif TRUE; + checkbox varid = OpalHiiConfig.KeepUserDataForced, + prompt = STRING_TOKEN(STR_NULL), + help = STRING_TOKEN(STR_NULL), + endcheckbox; + endif; + + subtitle text = STRING_TOKEN(STR_MAIN_OPAL_VERSION); + + subtitle text = STRING_TOKEN(STR_NULL); + + text + help = STRING_TOKEN(STR_NULL), + text = STRING_TOKEN(STR_DISK_INFO_SELECTED_DISK_NAME); + + subtitle text = STRING_TOKEN(STR_NULL); + + subtitle text = STRING_TOKEN(STR_OPAL_REQUESTS_LBL); + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_SET_ADMIN_PWD ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.Revert == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.SetAdminPwd, + prompt = STRING_TOKEN(STR_DISK_INFO_SET_ADMIN_PSWD), + help = STRING_TOKEN(STR_DISK_INFO_SET_ADMIN_PSWD_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x8005, + endcheckbox; + endif; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_SET_USER_PWD ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.DisableUser == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.Revert == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.SetUserPwd, + prompt = STRING_TOKEN(STR_DISK_INFO_SET_USER_PSWD), + help = STRING_TOKEN(STR_DISK_INFO_SET_USER_PSWD_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x8006, + endcheckbox; + endif; + endif; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_SECURE_ERASE ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.Revert == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.SecureErase, + prompt = STRING_TOKEN(STR_DISK_INFO_SECURE_ERASE), + help = STRING_TOKEN(STR_DISK_INFO_SECURE_ERASE_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x8007, + endcheckbox; + endif; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_REVERT ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.SetAdminPwd == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.SetUserPwd == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.SecureErase == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.DisableUser == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.Revert, + prompt = STRING_TOKEN(STR_DISK_INFO_REVERT), + help = STRING_TOKEN(STR_DISK_INFO_REVERT_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x8008, + endcheckbox; + endif; + endif; + endif; + endif; + endif; + endif; + + suppressif ideqval OpalHiiConfig.OpalRequest.Revert == 0; + grayoutif ideqval OpalHiiConfig.KeepUserDataForced == 1; + checkbox varid = OpalHiiConfig.OpalRequest.KeepUserData, + prompt = STRING_TOKEN(STR_KEEP_USER_DATA_PROMPT), + help = STRING_TOKEN(STR_KEEP_USER_DATA_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x8009, + endcheckbox; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_PSID_REVERT ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.SetAdminPwd == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.SetUserPwd == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.SecureErase == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.DisableUser == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.EnableFeature == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.Revert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.PsidRevert, + prompt = STRING_TOKEN(STR_DISK_INFO_PSID_REVERT), + help = STRING_TOKEN(STR_DISK_INFO_PSID_REVERT_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x800A, + endcheckbox; + endif; + endif; + endif; + endif; + endif; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_DISABLE_USER ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.SetUserPwd == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.Revert == 1; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.DisableUser, + prompt = STRING_TOKEN(STR_DISK_INFO_DISABLE_USER), + help = STRING_TOKEN(STR_DISK_INFO_DISABLE_USER_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x800B, + endcheckbox; + endif; + endif; + endif; + endif; + + suppressif ( questionref(SelectedDiskAvailableActions) & HII_ACTION_ENABLE_FEATURE ) == 0; + grayoutif ideqval OpalHiiConfig.OpalRequest.PsidRevert == 1; + checkbox varid = OpalHiiConfig.OpalRequest.EnableFeature, + prompt = STRING_TOKEN(STR_DISK_INFO_ENABLE_FEATURE), + help = STRING_TOKEN(STR_DISK_INFO_ENABLE_FEATURE_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = 0x800C, + endcheckbox; + endif; + endif; + +endform; // DISK INFO FORM + +endformset; diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c new file mode 100644 index 0000000000..7f9e14fa81 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c @@ -0,0 +1,944 @@ +/** @file + Opal Password PEI driver which is used to unlock Opal Password for S3. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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 "OpalPasswordPei.h" + +EFI_GUID mOpalDeviceAtaGuid = OPAL_DEVICE_ATA_GUID; +EFI_GUID mOpalDeviceNvmeGuid = OPAL_DEVICE_NVME_GUID; + +#define OPAL_PCIE_ROOTPORT_SAVESIZE (0x40) +#define STORE_INVALID_ROOTPORT_INDEX ((UINT8) -1) + +/** + Get IOMMU PPI. + + @return Pointer to IOMMU PPI. + +**/ +EDKII_IOMMU_PPI * +GetIoMmu ( + VOID + ) +{ + EFI_STATUS Status; + EDKII_IOMMU_PPI *IoMmu; + + IoMmu = NULL; + Status = PeiServicesLocatePpi ( + &gEdkiiIoMmuPpiGuid, + 0, + NULL, + (VOID **) &IoMmu + ); + if (!EFI_ERROR (Status) && (IoMmu != NULL)) { + return IoMmu; + } + + return NULL; +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + EFI_PHYSICAL_ADDRESS HostPhyAddress; + EDKII_IOMMU_PPI *IoMmu; + + *HostAddress = NULL; + *DeviceAddress = 0; + *Mapping = NULL; + + IoMmu = GetIoMmu (); + + if (IoMmu != NULL) { + Status = IoMmu->AllocateBuffer ( + IoMmu, + EfiBootServicesData, + Pages, + HostAddress, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + NumberOfBytes = EFI_PAGES_TO_SIZE (Pages); + Status = IoMmu->Map ( + IoMmu, + EdkiiIoMmuOperationBusMasterCommonBuffer, + *HostAddress, + &NumberOfBytes, + DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress); + *HostAddress = NULL; + return EFI_OUT_OF_RESOURCES; + } + Status = IoMmu->SetAttribute ( + IoMmu, + *Mapping, + EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE + ); + if (EFI_ERROR (Status)) { + IoMmu->Unmap (IoMmu, *Mapping); + IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress); + *Mapping = NULL; + *HostAddress = NULL; + return Status; + } + } else { + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &HostPhyAddress + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + *HostAddress = (VOID *) (UINTN) HostPhyAddress; + *DeviceAddress = HostPhyAddress; + *Mapping = NULL; + } + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + +**/ +VOID +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ) +{ + EDKII_IOMMU_PPI *IoMmu; + + IoMmu = GetIoMmu (); + + if (IoMmu != NULL) { + IoMmu->SetAttribute (IoMmu, Mapping, 0); + IoMmu->Unmap (IoMmu, Mapping); + IoMmu->FreeBuffer (IoMmu, Pages, HostAddress); + } else { + PeiServicesFreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, + Pages + ); + } +} + +/** + Provide IO action support. + + @param[in] PeiDev The opal device need to perform trusted IO. + @param[in] IoType OPAL_IO_TYPE indicating whether to perform a Trusted Send or Trusted Receive. + @param[in] SecurityProtocol Security Protocol + @param[in] SpSpecific Security Protocol Specific + @param[in] TransferLength Transfer Length of Buffer (in bytes) - always a multiple of 512 + @param[in] Buffer Address of Data to transfer + + @retval EFI_SUCCESS Perform the IO action success. + @retval Others Perform the IO action failed. + +**/ +EFI_STATUS +PerformTrustedIo ( + OPAL_PEI_DEVICE *PeiDev, + OPAL_IO_TYPE IoType, + UINT8 SecurityProtocol, + UINT16 SpSpecific, + UINTN TransferLength, + VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSizeBlocks; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + OPAL_DEVICE_ATA *DevInfoAta; + AHCI_CONTEXT *AhciContext; + NVME_CONTEXT *NvmeContext; + + Status = EFI_DEVICE_ERROR; + if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_ATA) { + DevInfoAta = (OPAL_DEVICE_ATA *) PeiDev->Device; + AhciContext = (AHCI_CONTEXT *) PeiDev->Context; + + BufferSizeBlocks = TransferLength / 512; + + ZeroMem( &AtaCommandBlock, sizeof( EFI_ATA_COMMAND_BLOCK ) ); + AtaCommandBlock.AtaCommand = ( IoType == OpalSend ) ? ATA_COMMAND_TRUSTED_SEND : ATA_COMMAND_TRUSTED_RECEIVE; + AtaCommandBlock.AtaSectorCount = ( UINT8 )BufferSizeBlocks; + AtaCommandBlock.AtaSectorNumber = ( UINT8 )( BufferSizeBlocks >> 8 ); + AtaCommandBlock.AtaFeatures = SecurityProtocol; + AtaCommandBlock.AtaCylinderLow = ( UINT8 )( SpSpecific >> 8 ); + AtaCommandBlock.AtaCylinderHigh = ( UINT8 )( SpSpecific ); + AtaCommandBlock.AtaDeviceHead = ATA_DEVICE_LBA; + + + ZeroMem( AhciContext->Buffer, HDD_PAYLOAD ); + ASSERT( TransferLength <= HDD_PAYLOAD ); + + if (IoType == OpalSend) { + CopyMem( AhciContext->Buffer, Buffer, TransferLength ); + } + + Status = AhciPioTransfer( + AhciContext, + (UINT8) DevInfoAta->Port, + (UINT8) DevInfoAta->PortMultiplierPort, + NULL, + 0, + ( IoType == OpalSend ) ? FALSE : TRUE, // i/o direction + &AtaCommandBlock, + NULL, + AhciContext->Buffer, + (UINT32)TransferLength, + ATA_TIMEOUT + ); + + if (IoType == OpalRecv) { + CopyMem( Buffer, AhciContext->Buffer, TransferLength ); + } + } else if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + NvmeContext = (NVME_CONTEXT *) PeiDev->Context; + Status = NvmeSecuritySendReceive ( + NvmeContext, + IoType == OpalSend, + SecurityProtocol, + SwapBytes16(SpSpecific), + TransferLength, + Buffer + ); + } else { + DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", PeiDev->DeviceType)); + } + + return Status; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +SecurityReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ) +{ + OPAL_PEI_DEVICE *PeiDev; + + PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This); + if (PeiDev == NULL) { + return EFI_DEVICE_ERROR; + } + + return PerformTrustedIo ( + PeiDev, + OpalRecv, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer + ); +} + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the send data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +SecuritySendData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer + ) +{ + OPAL_PEI_DEVICE *PeiDev; + + PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This); + if (PeiDev == NULL) { + return EFI_DEVICE_ERROR; + } + + return PerformTrustedIo ( + PeiDev, + OpalSend, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer + ); + +} + +/** + Save/Restore RootPort configuration space. + + @param[in] DevInfoNvme Pointer to NVMe device info. + @param[in] SaveAction TRUE: Save, FALSE: Restore + @param[in,out] PcieConfBufferList Configuration space data buffer for save/restore + + @return PCIE base address of this RootPort +**/ +UINTN +SaveRestoreRootportConfSpace ( + IN OPAL_DEVICE_NVME *DevInfoNvme, + IN BOOLEAN SaveAction, + IN OUT UINT8 **PcieConfBufferList + ) +{ + UINTN RpBase; + UINTN Length; + OPAL_PCI_DEVICE *DevNode; + UINT8 *StorePcieConfData; + UINTN Index; + + Length = 0; + Index = 0; + RpBase = 0; + + while (sizeof (OPAL_DEVICE_NVME) + Length < DevInfoNvme->Length) { + DevNode = (OPAL_PCI_DEVICE *)((UINT8*)DevInfoNvme->PciBridgeNode + Length); + RpBase = PCI_LIB_ADDRESS (DevNode->Bus, DevNode->Device, DevNode->Function, 0x0); + + if (PcieConfBufferList != NULL) { + if (SaveAction) { + StorePcieConfData = (UINT8 *) AllocateZeroPool (OPAL_PCIE_ROOTPORT_SAVESIZE); + ASSERT (StorePcieConfData != NULL); + OpalPciRead (StorePcieConfData, RpBase, OPAL_PCIE_ROOTPORT_SAVESIZE); + PcieConfBufferList[Index] = StorePcieConfData; + } else { + // Skip PCIe Command & Status registers + StorePcieConfData = PcieConfBufferList[Index]; + OpalPciWrite (RpBase, StorePcieConfData, 4); + OpalPciWrite (RpBase + 8, StorePcieConfData + 8, OPAL_PCIE_ROOTPORT_SAVESIZE - 8); + + FreePool (StorePcieConfData); + } + } + + Length += sizeof (OPAL_PCI_DEVICE); + Index ++; + } + + return RpBase; +} + +/** + Configure RootPort for downstream PCIe NAND devices. + + @param[in] RpBase - PCIe configuration space address of this RootPort + @param[in] BusNumber - Bus number + @param[in] MemoryBase - Memory base address + @param[in] MemoryLength - Memory size + +**/ +VOID +ConfigureRootPortForPcieNand ( + IN UINTN RpBase, + IN UINTN BusNumber, + IN UINT32 MemoryBase, + IN UINT32 MemoryLength + ) +{ + UINT32 MemoryLimit; + + DEBUG ((DEBUG_INFO, "ConfigureRootPortForPcieNand, BusNumber: %x, MemoryBase: %x, MemoryLength: %x\n", + BusNumber, MemoryBase, MemoryLength)); + + if (MemoryLength == 0) { + MemoryLimit = MemoryBase; + } else { + MemoryLimit = MemoryBase + MemoryLength + 0xFFFFF; // 1M + } + + /// + /// Configue PCIE configuration space for RootPort + /// + PciWrite8 (RpBase + NVME_PCIE_BNUM + 1, (UINT8) BusNumber); // Secondary Bus Number registers + PciWrite8 (RpBase + NVME_PCIE_BNUM + 2, (UINT8) BusNumber); // Subordinate Bus Number registers + PciWrite8 (RpBase + NVME_PCIE_IOBL, 0xFF); // I/O Base registers + PciWrite8 (RpBase + NVME_PCIE_IOBL + 1, 0x00); // I/O Limit registers + PciWrite16 (RpBase + NVME_PCIE_MBL, (UINT16) RShiftU64 ((UINTN)MemoryBase, 16)); // Memory Base register + PciWrite16 (RpBase + NVME_PCIE_MBL + 2, (UINT16) RShiftU64 ((UINTN)MemoryLimit, 16)); // Memory Limit register + PciWrite16 (RpBase + NVME_PCIE_PMBL, 0xFFFF); // Prefetchable Memory Base registers + PciWrite16 (RpBase + NVME_PCIE_PMBL + 2, 0x0000); // Prefetchable Memory Limit registers + PciWrite32 (RpBase + NVME_PCIE_PMBU32, 0xFFFFFFFF); // Prefetchable Memory Upper Base registers + PciWrite32 (RpBase + NVME_PCIE_PMLU32, 0x00000000); // Prefetchable Memory Upper Limit registers +} + +/** + + The function returns whether or not the device is Opal Locked. + TRUE means that the device is partially or fully locked. + This will perform a Level 0 Discovery and parse the locking feature descriptor + + @param[in] OpalDev Opal object to determine if locked. + @param[out] BlockSidSupported Whether device support BlockSid feature. + +**/ +BOOLEAN +IsOpalDeviceLocked( + OPAL_PEI_DEVICE *OpalDev, + BOOLEAN *BlockSidSupported + ) +{ + OPAL_SESSION Session; + OPAL_DISK_SUPPORT_ATTRIBUTE SupportedAttributes; + TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature; + UINT16 OpalBaseComId; + TCG_RESULT Ret; + + Session.Sscp = &OpalDev->Sscp; + Session.MediaId = 0; + + Ret = OpalGetSupportedAttributesInfo (&Session, &SupportedAttributes, &OpalBaseComId); + if (Ret != TcgResultSuccess) { + return FALSE; + } + + Session.OpalBaseComId = OpalBaseComId; + *BlockSidSupported = SupportedAttributes.BlockSid == 1 ? TRUE : FALSE; + + Ret = OpalGetLockingInfo(&Session, &LockingFeature); + if (Ret != TcgResultSuccess) { + return FALSE; + } + + return OpalDeviceLocked (&SupportedAttributes, &LockingFeature); +} + +/** + Unlock OPAL password for S3. + + @param[in] OpalDev Opal object to unlock. + +**/ +VOID +UnlockOpalPassword ( + IN OPAL_PEI_DEVICE *OpalDev + ) +{ + TCG_RESULT Result; + OPAL_SESSION Session; + BOOLEAN BlockSidSupport; + UINT32 PpStorageFlags; + BOOLEAN BlockSIDEnabled; + + BlockSidSupport = FALSE; + if (IsOpalDeviceLocked (OpalDev, &BlockSidSupport)) { + ZeroMem(&Session, sizeof (Session)); + Session.Sscp = &OpalDev->Sscp; + Session.MediaId = 0; + Session.OpalBaseComId = OpalDev->Device->OpalBaseComId; + + Result = OpalUtilUpdateGlobalLockingRange ( + &Session, + OpalDev->Device->Password, + OpalDev->Device->PasswordLength, + FALSE, + FALSE + ); + DEBUG (( + DEBUG_INFO, + "%a() OpalUtilUpdateGlobalLockingRange() Result = 0x%x\n", + __FUNCTION__, + Result + )); + } + + PpStorageFlags = Tcg2PhysicalPresenceLibGetManagementFlags (); + if ((PpStorageFlags & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) { + BlockSIDEnabled = TRUE; + } else { + BlockSIDEnabled = FALSE; + } + if (BlockSIDEnabled && BlockSidSupport) { + ZeroMem(&Session, sizeof (Session)); + Session.Sscp = &OpalDev->Sscp; + Session.MediaId = 0; + Session.OpalBaseComId = OpalDev->Device->OpalBaseComId; + Result = OpalBlockSid (&Session, TRUE); + DEBUG (( + DEBUG_INFO, + "%a() OpalBlockSid() Result = 0x%x\n", + __FUNCTION__, + Result + )); + } +} + +/** + Unlock ATA OPAL password for S3. + +**/ +VOID +UnlockOpalPasswordAta ( + VOID + ) +{ + EFI_STATUS Status; + UINT8 *DevInfo; + OPAL_DEVICE_ATA TempDevInfoAta; + OPAL_DEVICE_ATA *DevInfoAta; + UINTN DevInfoLengthAta; + UINT8 Bus; + UINT8 Device; + UINT8 Function; + OPAL_PEI_DEVICE OpalDev; + UINT8 BaseClassCode; + UINT8 SubClassCode; + UINT8 SataCmdSt; + AHCI_CONTEXT AhciContext; + UINT32 AhciBar; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + // + // Get ATA OPAL device info from LockBox. + // + DevInfo = (UINT8 *) &TempDevInfoAta; + DevInfoLengthAta = sizeof (OPAL_DEVICE_ATA); + Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta); + if (Status == EFI_BUFFER_TOO_SMALL) { + DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthAta)); + if (DevInfo != NULL) { + Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta); + } + } + if (EFI_ERROR (Status) || (DevInfo == NULL)) { + return; + } + + for (DevInfoAta = (OPAL_DEVICE_ATA *) DevInfo; + (UINTN) DevInfoAta < ((UINTN) DevInfo + DevInfoLengthAta); + DevInfoAta = (OPAL_DEVICE_ATA *) ((UINTN) DevInfoAta + DevInfoAta->Length)) { + Bus = DevInfoAta->Device.Bus; + Device = DevInfoAta->Device.Device; + Function = DevInfoAta->Device.Function; + + SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET)); + PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), 0x6); + + BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B)); + SubClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A)); + if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) || + ((SubClassCode != PCI_CLASS_MASS_STORAGE_SATADPA) && (SubClassCode != PCI_CLASS_MASS_STORAGE_RAID))) { + DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode are not supported\n", __FUNCTION__)); + } else { + AhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24)); + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), DevInfoAta->BarAddr); + + ZeroMem (&AhciContext, sizeof (AHCI_CONTEXT)); + AhciContext.AhciBar = DevInfoAta->BarAddr; + AhciAllocateResource (&AhciContext); + Status = AhciModeInitialize (&AhciContext, (UINT8)DevInfoAta->Port); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a() AhciModeInitialize() error, Status: %r\n", __FUNCTION__, Status)); + } + + OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE; + OpalDev.Sscp.ReceiveData = SecurityReceiveData; + OpalDev.Sscp.SendData = SecuritySendData; + OpalDev.DeviceType = OPAL_DEVICE_TYPE_ATA; + OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoAta; + OpalDev.Context = &AhciContext; + + UnlockOpalPassword (&OpalDev); + + AhciFreeResource (&AhciContext); + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), AhciBar); + } + PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), SataCmdSt); + } + + ZeroMem (DevInfo, DevInfoLengthAta); + if ((UINTN) DevInfo != (UINTN) &TempDevInfoAta) { + FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthAta)); + } + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); +} + +/** + Unlock NVMe OPAL password for S3. + +**/ +VOID +UnlockOpalPasswordNvme ( + VOID + ) +{ + EFI_STATUS Status; + UINT8 *DevInfo; + OPAL_DEVICE_NVME TempDevInfoNvme; + OPAL_DEVICE_NVME *DevInfoNvme; + UINTN DevInfoLengthNvme; + UINT8 Bus; + UINT8 Device; + UINT8 Function; + OPAL_PEI_DEVICE OpalDev; + UINT8 BaseClassCode; + UINT8 SubClassCode; + UINT8 ProgInt; + UINT8 NvmeCmdSt; + UINT8 *StorePcieConfDataList[16]; + UINTN RpBase; + UINTN MemoryBase; + UINTN MemoryLength; + NVME_CONTEXT NvmeContext; + + DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__)); + + // + // Get NVMe OPAL device info from LockBox. + // + DevInfo = (UINT8 *) &TempDevInfoNvme; + DevInfoLengthNvme = sizeof (OPAL_DEVICE_NVME); + Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme); + if (Status == EFI_BUFFER_TOO_SMALL) { + DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthNvme)); + if (DevInfo != NULL) { + Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme); + } + } + if (EFI_ERROR (Status) || (DevInfo == NULL)) { + return; + } + + for (DevInfoNvme = (OPAL_DEVICE_NVME *) DevInfo; + (UINTN) DevInfoNvme < ((UINTN) DevInfo + DevInfoLengthNvme); + DevInfoNvme = (OPAL_DEVICE_NVME *) ((UINTN) DevInfoNvme + DevInfoNvme->Length)) { + Bus = DevInfoNvme->Device.Bus; + Device = DevInfoNvme->Device.Device; + Function = DevInfoNvme->Device.Function; + + RpBase = 0; + NvmeCmdSt = 0; + + /// + /// Save original RootPort configuration space to heap + /// + RpBase = SaveRestoreRootportConfSpace ( + DevInfoNvme, + TRUE, // save + StorePcieConfDataList + ); + MemoryBase = DevInfoNvme->BarAddr; + MemoryLength = 0; + ConfigureRootPortForPcieNand (RpBase, Bus, (UINT32) MemoryBase, (UINT32) MemoryLength); + + /// + /// Enable PCIE decode for RootPort + /// + NvmeCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0x6); + + BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B)); + SubClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A)); + ProgInt = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x09)); + if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) || + (SubClassCode != PCI_CLASS_MASS_STORAGE_NVM) || + (ProgInt != PCI_IF_NVMHCI)) { + DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode/PI are not supported\n", __FUNCTION__)); + } else { + ZeroMem (&NvmeContext, sizeof (NVME_CONTEXT)); + NvmeContext.Nbar = DevInfoNvme->BarAddr; + NvmeContext.PciBase = PCI_LIB_ADDRESS (Bus, Device, Function, 0x0); + NvmeContext.NvmeInitWaitTime = 0; + NvmeContext.Nsid = DevInfoNvme->NvmeNamespaceId; + NvmeAllocateResource (&NvmeContext); + Status = NvmeControllerInit (&NvmeContext); + + OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE; + OpalDev.Sscp.ReceiveData = SecurityReceiveData; + OpalDev.Sscp.SendData = SecuritySendData; + OpalDev.DeviceType = OPAL_DEVICE_TYPE_NVME; + OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoNvme; + OpalDev.Context = &NvmeContext; + + UnlockOpalPassword (&OpalDev); + + Status = NvmeControllerExit (&NvmeContext); + NvmeFreeResource (&NvmeContext); + } + + ASSERT (RpBase != 0); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0); + RpBase = SaveRestoreRootportConfSpace ( + DevInfoNvme, + FALSE, // restore + StorePcieConfDataList + ); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, NvmeCmdSt); + } + + ZeroMem (DevInfo, DevInfoLengthNvme); + if ((UINTN) DevInfo != (UINTN) &TempDevInfoNvme) { + FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthNvme)); + } + + DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__)); +} + +/** + Unlock OPAL password for S3. + +**/ +VOID +OpalPasswordS3 ( + VOID + ) +{ + UnlockOpalPasswordAta (); + UnlockOpalPasswordNvme (); +} + +/** + Entry point of the notification callback function itself within the PEIM. + It is to unlock OPAL 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 +OpalPasswordEndOfPeiNotify( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + + Status = PeiServicesGetBootMode (&BootMode); + ASSERT_EFI_ERROR (Status); + if (BootMode != BOOT_ON_S3_RESUME) { + return EFI_UNSUPPORTED; + } + + DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__)); + + OpalPasswordS3 (); + + DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__)); + + return EFI_SUCCESS; +} + +EFI_PEI_NOTIFY_DESCRIPTOR mOpalPasswordEndOfPeiNotifyDesc = { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + OpalPasswordEndOfPeiNotify +}; + +/** + 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 +OpalPasswordPeiInit ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + Status = PeiServicesNotifyPpi (&mOpalPasswordEndOfPeiNotifyDesc); + ASSERT_EFI_ERROR (Status); + return Status; +} + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.h b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.h new file mode 100644 index 0000000000..31aab37f5d --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.h @@ -0,0 +1,133 @@ +/** @file + Opal Password PEI driver which is used to unlock Opal Password for S3. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _OPAL_PASSWORD_PEI_H_ +#define _OPAL_PASSWORD_PEI_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "OpalPasswordCommon.h" +#include "OpalAhciMode.h" +#include "OpalNvmeMode.h" + +// +// Time out Value for ATA pass through protocol +// +#define ATA_TIMEOUT 30000000 + +// +// The payload Length of HDD related ATA commands +// +#define HDD_PAYLOAD 512 +// +// According to ATA spec, the max Length of hdd password is 32 bytes +// +#define OPAL_PASSWORD_MAX_LENGTH 32 + +#pragma pack(1) + +/** +* Opal I/O Type utilized by the Trusted IO callback +* +* The type indicates if the I/O is a send or receive +*/ +typedef enum { + // + // I/O is a TCG Trusted Send command + // + OpalSend, + + // + // I/O is a TCG Trusted Receive command + // + OpalRecv +} OPAL_IO_TYPE; + +#define OPAL_PEI_DEVICE_SIGNATURE SIGNATURE_32 ('o', 'p', 'd', 's') + +typedef struct { + UINTN Signature; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL Sscp; + UINT8 DeviceType; + OPAL_DEVICE_COMMON *Device; + VOID *Context; +} OPAL_PEI_DEVICE; + +#define OPAL_PEI_DEVICE_FROM_THIS(a) CR (a, OPAL_PEI_DEVICE, Sscp, OPAL_PEI_DEVICE_SIGNATURE) + +#pragma pack() + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + +**/ +VOID +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ); + +#endif // _OPAL_PASSWORD_PEI_H_ + diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf new file mode 100644 index 0000000000..81c57c36d2 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf @@ -0,0 +1,63 @@ +## @file +# This is a Opal Password PEI driver. +# +# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OpalPasswordPei + FILE_GUID = DED60489-979C-4B5A-8EE4-4068B0CC38DC + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = OpalPasswordPeiInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + OpalPasswordPei.c + OpalPasswordPei.h + OpalPasswordCommon.h + OpalAhciMode.c + OpalAhciMode.h + OpalNvmeMode.c + OpalNvmeMode.h + OpalNvmeReg.h + +[Packages] + MdePkg/MdePkg.dec + SecurityPkg/SecurityPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + DebugLib + IoLib + PciLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + TimerLib + HobLib + LockBoxLib + TcgStorageOpalLib + Tcg2PhysicalPresenceLib + +[Ppis] + gEdkiiIoMmuPpiGuid ## SOMETIMES_CONSUMES + gEfiEndOfPeiSignalPpiGuid ## NOTIFY + +[Depex] + gEfiPeiMasterBootModePpiGuid