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