/** @file | |
TCG MOR (Memory Overwrite Request) Control Driver. | |
This driver initialize MemoryOverwriteRequestControl variable. It | |
will clear MOR_CLEAR_MEMORY_BIT bit if it is set. It will also do TPer Reset for | |
those encrypted drives through EFI_STORAGE_SECURITY_COMMAND_PROTOCOL at EndOfDxe. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "TcgMor.h" | |
UINT8 mMorControl; | |
/** | |
Ready to Boot Event notification handler. | |
@param[in] Event Event whose notification function is being invoked | |
@param[in] Context Pointer to the notification function's context | |
**/ | |
VOID | |
EFIAPI | |
OnReadyToBoot ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN DataSize; | |
if (MOR_CLEAR_MEMORY_VALUE (mMorControl) == 0x0) { | |
// | |
// MorControl is expected, directly return to avoid unnecessary variable operation | |
// | |
return; | |
} | |
// | |
// Clear MOR_CLEAR_MEMORY_BIT | |
// | |
DEBUG ((DEBUG_INFO, "TcgMor: Clear MorClearMemory bit\n")); | |
mMorControl &= 0xFE; | |
DataSize = sizeof (mMorControl); | |
Status = gRT->SetVariable ( | |
MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, | |
&gEfiMemoryOverwriteControlDataGuid, | |
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
DataSize, | |
&mMorControl | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "TcgMor: Clear MOR_CLEAR_MEMORY_BIT failure, Status = %r\n", Status)); | |
} | |
} | |
/** | |
Send TPer Reset command to reset eDrive to lock all protected bands. | |
Typically, there are 2 mechanism for resetting eDrive. They are: | |
1. TPer Reset through IEEE 1667 protocol. | |
2. TPer Reset through native TCG protocol. | |
This routine will detect what protocol the attached eDrive conform to, TCG or | |
IEEE 1667 protocol. Then send out TPer Reset command separately. | |
@param[in] Ssp The pointer to EFI_STORAGE_SECURITY_COMMAND_PROTOCOL instance. | |
@param[in] MediaId ID of the medium to receive data from or send data to. | |
**/ | |
VOID | |
InitiateTPerReset ( | |
IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *Ssp, | |
IN UINT32 MediaId | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 *Buffer; | |
UINTN XferSize; | |
UINTN Len; | |
UINTN Index; | |
BOOLEAN TcgFlag; | |
BOOLEAN IeeeFlag; | |
SUPPORTED_SECURITY_PROTOCOLS_PARAMETER_DATA *Data; | |
Buffer = NULL; | |
TcgFlag = FALSE; | |
IeeeFlag = FALSE; | |
// | |
// ATA8-ACS 7.57.6.1 indicates the Transfer Length field requirements a multiple of 512. | |
// If the length of the TRUSTED RECEIVE parameter data is greater than the Transfer Length, | |
// then the device shall return the TRUSTED RECEIVE parameter data truncated to the requested Transfer Length. | |
// | |
Len = ROUNDUP512 (sizeof (SUPPORTED_SECURITY_PROTOCOLS_PARAMETER_DATA)); | |
Buffer = AllocateZeroPool (Len); | |
if (Buffer == NULL) { | |
return; | |
} | |
// | |
// When the Security Protocol field is set to 00h, and SP Specific is set to 0000h in a TRUSTED RECEIVE | |
// command, the device basic information data shall be returned. | |
// | |
Status = Ssp->ReceiveData ( | |
Ssp, | |
MediaId, | |
100000000, // Timeout 10-sec | |
0, // SecurityProtocol | |
0, // SecurityProtocolSpecificData | |
Len, // PayloadBufferSize, | |
Buffer, // PayloadBuffer | |
&XferSize | |
); | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
// | |
// In returned data, the ListLength field indicates the total length, in bytes, | |
// of the supported security protocol list. | |
// | |
Data = (SUPPORTED_SECURITY_PROTOCOLS_PARAMETER_DATA *)Buffer; | |
Len = ROUNDUP512 ( | |
sizeof (SUPPORTED_SECURITY_PROTOCOLS_PARAMETER_DATA) + | |
(Data->SupportedSecurityListLength[0] << 8) + | |
(Data->SupportedSecurityListLength[1]) | |
); | |
// | |
// Free original buffer and allocate new buffer. | |
// | |
FreePool (Buffer); | |
Buffer = AllocateZeroPool (Len); | |
if (Buffer == NULL) { | |
return; | |
} | |
// | |
// Read full supported security protocol list from device. | |
// | |
Status = Ssp->ReceiveData ( | |
Ssp, | |
MediaId, | |
100000000, // Timeout 10-sec | |
0, // SecurityProtocol | |
0, // SecurityProtocolSpecificData | |
Len, // PayloadBufferSize, | |
Buffer, // PayloadBuffer | |
&XferSize | |
); | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
Data = (SUPPORTED_SECURITY_PROTOCOLS_PARAMETER_DATA *)Buffer; | |
Len = (Data->SupportedSecurityListLength[0] << 8) + Data->SupportedSecurityListLength[1]; | |
// | |
// Iterate full supported security protocol list to check if TCG or IEEE 1667 protocol | |
// is supported. | |
// | |
for (Index = 0; Index < Len; Index++) { | |
if (Data->SupportedSecurityProtocol[Index] == SECURITY_PROTOCOL_TCG) { | |
// | |
// Found a TCG device. | |
// | |
TcgFlag = TRUE; | |
DEBUG ((DEBUG_INFO, "This device is a TCG protocol device\n")); | |
break; | |
} | |
if (Data->SupportedSecurityProtocol[Index] == SECURITY_PROTOCOL_IEEE1667) { | |
// | |
// Found a IEEE 1667 device. | |
// | |
IeeeFlag = TRUE; | |
DEBUG ((DEBUG_INFO, "This device is a IEEE 1667 protocol device\n")); | |
break; | |
} | |
} | |
if (!TcgFlag && !IeeeFlag) { | |
DEBUG ((DEBUG_INFO, "Neither a TCG nor IEEE 1667 protocol device is found\n")); | |
goto Exit; | |
} | |
if (TcgFlag) { | |
// | |
// As long as TCG protocol is supported, send out a TPer Reset | |
// TCG command to the device via the TrustedSend command with a non-zero Transfer Length. | |
// | |
Status = Ssp->SendData ( | |
Ssp, | |
MediaId, | |
100000000, // Timeout 10-sec | |
SECURITY_PROTOCOL_TCG, // SecurityProtocol | |
0x0400, // SecurityProtocolSpecificData | |
512, // PayloadBufferSize, | |
Buffer // PayloadBuffer | |
); | |
if (!EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "Send TPer Reset Command Successfully !\n")); | |
} else { | |
DEBUG ((DEBUG_INFO, "Send TPer Reset Command Fail !\n")); | |
} | |
} | |
if (IeeeFlag) { | |
// | |
// TBD : Perform a TPer Reset via IEEE 1667 Protocol | |
// | |
DEBUG ((DEBUG_INFO, "IEEE 1667 Protocol didn't support yet!\n")); | |
} | |
Exit: | |
if (Buffer != NULL) { | |
FreePool (Buffer); | |
} | |
} | |
/** | |
Notification function of END_OF_DXE. | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
TPerResetAtEndOfDxe ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *Ssp; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_STATUS Status; | |
UINTN HandleCount; | |
EFI_HANDLE *HandleBuffer; | |
UINTN Index; | |
// | |
// Locate all SSP protocol instances. | |
// | |
HandleCount = 0; | |
HandleBuffer = NULL; | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiStorageSecurityCommandProtocolGuid, | |
NULL, | |
&HandleCount, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status) || (HandleCount == 0) || (HandleBuffer == NULL)) { | |
return; | |
} | |
for (Index = 0; Index < HandleCount; Index++) { | |
// | |
// Get the SSP interface. | |
// | |
Status = gBS->HandleProtocol ( | |
HandleBuffer[Index], | |
&gEfiStorageSecurityCommandProtocolGuid, | |
(VOID **)&Ssp | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
Status = gBS->HandleProtocol ( | |
HandleBuffer[Index], | |
&gEfiBlockIoProtocolGuid, | |
(VOID **)&BlockIo | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
InitiateTPerReset (Ssp, BlockIo->Media->MediaId); | |
} | |
FreePool (HandleBuffer); | |
} | |
/** | |
Entry Point for TCG MOR Control driver. | |
@param[in] ImageHandle Image handle of this driver. | |
@param[in] SystemTable A Pointer to the EFI System Table. | |
@retval EFI_SUCCESS | |
@return Others Some error occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MorDriverEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN DataSize; | |
EFI_EVENT Event; | |
/// | |
/// The firmware is required to create the MemoryOverwriteRequestControl UEFI variable. | |
/// | |
DataSize = sizeof (mMorControl); | |
Status = gRT->GetVariable ( | |
MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, | |
&gEfiMemoryOverwriteControlDataGuid, | |
NULL, | |
&DataSize, | |
&mMorControl | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Set default value to 0 | |
// | |
mMorControl = 0; | |
Status = gRT->SetVariable ( | |
MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, | |
&gEfiMemoryOverwriteControlDataGuid, | |
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
DataSize, | |
&mMorControl | |
); | |
DEBUG ((DEBUG_INFO, "TcgMor: Create MOR variable! Status = %r\n", Status)); | |
} else { | |
// | |
// Create a Ready To Boot Event and Clear the MorControl bit in the call back function. | |
// | |
DEBUG ((DEBUG_INFO, "TcgMor: Create ReadyToBoot Event for MorControl Bit cleaning!\n")); | |
Status = EfiCreateEventReadyToBootEx ( | |
TPL_CALLBACK, | |
OnReadyToBoot, | |
NULL, | |
&Event | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Register EFI_END_OF_DXE_EVENT_GROUP_GUID event. | |
// | |
DEBUG ((DEBUG_INFO, "TcgMor: Create EndofDxe Event for Mor TPer Reset!\n")); | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
TPerResetAtEndOfDxe, | |
NULL, | |
&gEfiEndOfDxeEventGroupGuid, | |
&Event | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return Status; | |
} |