/** @file | |
This file implements ATA pass through transaction for ATA bus driver. | |
This file implements the low level execution of ATA pass through transaction. | |
It transforms the high level identity, read/write, reset command to ATA pass | |
through command and protocol. | |
NOTE: This file also implements the StorageSecurityCommandProtocol(SSP). For input | |
parameter SecurityProtocolSpecificData, ATA spec has no explicitly definition | |
for Security Protocol Specific layout. This implementation uses big endian for | |
Cylinder register. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "AtaBus.h" | |
#define ATA_CMD_TRUST_NON_DATA 0x5B | |
#define ATA_CMD_TRUST_RECEIVE 0x5C | |
#define ATA_CMD_TRUST_RECEIVE_DMA 0x5D | |
#define ATA_CMD_TRUST_SEND 0x5E | |
#define ATA_CMD_TRUST_SEND_DMA 0x5F | |
// | |
// Look up table (UdmaValid, IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL | |
// | |
EFI_ATA_PASS_THRU_CMD_PROTOCOL mAtaPassThruCmdProtocols[][2] = { | |
{ | |
EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN, | |
EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT | |
}, | |
{ | |
EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN, | |
EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT, | |
} | |
}; | |
// | |
// Look up table (UdmaValid, Lba48Bit, IsIsWrite) for ATA_CMD | |
// | |
UINT8 mAtaCommands[][2][2] = { | |
{ | |
{ | |
ATA_CMD_READ_SECTORS, // 28-bit LBA; PIO read | |
ATA_CMD_WRITE_SECTORS // 28-bit LBA; PIO write | |
}, | |
{ | |
ATA_CMD_READ_SECTORS_EXT, // 48-bit LBA; PIO read | |
ATA_CMD_WRITE_SECTORS_EXT // 48-bit LBA; PIO write | |
} | |
}, | |
{ | |
{ | |
ATA_CMD_READ_DMA, // 28-bit LBA; DMA read | |
ATA_CMD_WRITE_DMA // 28-bit LBA; DMA write | |
}, | |
{ | |
ATA_CMD_READ_DMA_EXT, // 48-bit LBA; DMA read | |
ATA_CMD_WRITE_DMA_EXT // 48-bit LBA; DMA write | |
} | |
} | |
}; | |
// | |
// Look up table (UdmaValid, IsTrustSend) for ATA_CMD | |
// | |
UINT8 mAtaTrustCommands[2][2] = { | |
{ | |
ATA_CMD_TRUST_RECEIVE, // PIO read | |
ATA_CMD_TRUST_SEND // PIO write | |
}, | |
{ | |
ATA_CMD_TRUST_RECEIVE_DMA, // DMA read | |
ATA_CMD_TRUST_SEND_DMA // DMA write | |
} | |
}; | |
// | |
// Look up table (Lba48Bit) for maximum transfer block number | |
// | |
UINTN mMaxTransferBlockNumber[] = { | |
MAX_28BIT_TRANSFER_BLOCK_NUM, | |
MAX_48BIT_TRANSFER_BLOCK_NUM | |
}; | |
/** | |
Wrapper for EFI_ATA_PASS_THRU_PROTOCOL.PassThru(). | |
This function wraps the PassThru() invocation for ATA pass through function | |
for an ATA device. It assembles the ATA pass through command packet for ATA | |
transaction. | |
@param[in, out] AtaDevice The ATA child device involved for the operation. | |
@param[in, out] TaskPacket Pointer to a Pass Thru Command Packet. Optional, | |
if it is NULL, blocking mode, and use the packet | |
in AtaDevice. If it is not NULL, non blocking mode, | |
and pass down this Packet. | |
@param[in, out] Event If Event is NULL, then blocking I/O is performed. | |
If Event is not NULL and non-blocking I/O is | |
supported,then non-blocking I/O is performed, | |
and Event will be signaled when the write | |
request is completed. | |
@return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru(). | |
**/ | |
EFI_STATUS | |
AtaDevicePassThru ( | |
IN OUT ATA_DEVICE *AtaDevice, | |
IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *TaskPacket OPTIONAL, | |
IN OUT EFI_EVENT Event OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; | |
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet; | |
// | |
// Assemble packet. If it is non blocking mode, the Ata driver should keep each | |
// subtask and clean them when the event is signaled. | |
// | |
if (TaskPacket != NULL) { | |
Packet = TaskPacket; | |
Packet->Asb = AllocateAlignedBuffer (AtaDevice, sizeof (EFI_ATA_STATUS_BLOCK)); | |
if (Packet->Asb == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CopyMem (Packet->Asb, AtaDevice->Asb, sizeof (EFI_ATA_STATUS_BLOCK)); | |
Packet->Acb = AllocateCopyPool (sizeof (EFI_ATA_COMMAND_BLOCK), &AtaDevice->Acb); | |
} else { | |
Packet = &AtaDevice->Packet; | |
Packet->Asb = AtaDevice->Asb; | |
Packet->Acb = &AtaDevice->Acb; | |
} | |
AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru; | |
Status = AtaPassThru->PassThru ( | |
AtaPassThru, | |
AtaDevice->Port, | |
AtaDevice->PortMultiplierPort, | |
Packet, | |
Event | |
); | |
// | |
// Ensure ATA pass through caller and callee have the same | |
// interpretation of ATA pass through protocol. | |
// | |
ASSERT (Status != EFI_INVALID_PARAMETER); | |
ASSERT (Status != EFI_BAD_BUFFER_SIZE); | |
return Status; | |
} | |
/** | |
Wrapper for EFI_ATA_PASS_THRU_PROTOCOL.ResetDevice(). | |
This function wraps the ResetDevice() invocation for ATA pass through function | |
for an ATA device. | |
@param AtaDevice The ATA child device involved for the operation. | |
@return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru(). | |
**/ | |
EFI_STATUS | |
ResetAtaDevice ( | |
IN ATA_DEVICE *AtaDevice | |
) | |
{ | |
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; | |
AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru; | |
// | |
// Report Status Code to indicate reset happens | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), | |
AtaDevice->AtaBusDriverData->ParentDevicePath | |
); | |
return AtaPassThru->ResetDevice ( | |
AtaPassThru, | |
AtaDevice->Port, | |
AtaDevice->PortMultiplierPort | |
); | |
} | |
/** | |
Prints ATA model name to ATA device structure. | |
This function converts ATA device model name from ATA identify data | |
to a string in ATA device structure. It needs to change the character | |
order in the original model name string. | |
@param AtaDevice The ATA child device involved for the operation. | |
**/ | |
VOID | |
PrintAtaModelName ( | |
IN OUT ATA_DEVICE *AtaDevice | |
) | |
{ | |
UINTN Index; | |
CHAR8 *Source; | |
CHAR16 *Destination; | |
Source = AtaDevice->IdentifyData->ModelName; | |
Destination = AtaDevice->ModelName; | |
// | |
// Swap the byte order in the original module name. | |
// | |
for (Index = 0; Index < MAX_MODEL_NAME_LEN; Index += 2) { | |
Destination[Index] = Source[Index + 1]; | |
Destination[Index + 1] = Source[Index]; | |
} | |
AtaDevice->ModelName[MAX_MODEL_NAME_LEN] = L'\0'; | |
} | |
/** | |
Gets ATA device Capacity according to ATA 6. | |
This function returns the capacity of the ATA device if it follows | |
ATA 6 to support 48 bit addressing. | |
@param AtaDevice The ATA child device involved for the operation. | |
@return The capacity of the ATA device or 0 if the device does not support | |
48-bit addressing defined in ATA 6. | |
**/ | |
EFI_LBA | |
GetAtapi6Capacity ( | |
IN ATA_DEVICE *AtaDevice | |
) | |
{ | |
EFI_LBA Capacity; | |
EFI_LBA TmpLba; | |
UINTN Index; | |
ATA_IDENTIFY_DATA *IdentifyData; | |
IdentifyData = AtaDevice->IdentifyData; | |
if ((IdentifyData->command_set_supported_83 & BIT10) == 0) { | |
// | |
// The device doesn't support 48 bit addressing | |
// | |
return 0; | |
} | |
// | |
// 48 bit address feature set is supported, get maximum capacity | |
// | |
Capacity = 0; | |
for (Index = 0; Index < 4; Index++) { | |
// | |
// Lower byte goes first: word[100] is the lowest word, word[103] is highest | |
// | |
TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index]; | |
Capacity |= LShiftU64 (TmpLba, 16 * Index); | |
} | |
return Capacity; | |
} | |
/** | |
Identifies ATA device via the Identify data. | |
This function identifies the ATA device and initializes the Media information in | |
Block IO protocol interface. | |
@param AtaDevice The ATA child device involved for the operation. | |
@retval EFI_UNSUPPORTED The device is not a valid ATA device (hard disk). | |
@retval EFI_SUCCESS The device is successfully identified and Media information | |
is correctly initialized. | |
**/ | |
EFI_STATUS | |
IdentifyAtaDevice ( | |
IN OUT ATA_DEVICE *AtaDevice | |
) | |
{ | |
ATA_IDENTIFY_DATA *IdentifyData; | |
EFI_BLOCK_IO_MEDIA *BlockMedia; | |
EFI_LBA Capacity; | |
UINT16 PhyLogicSectorSupport; | |
UINT16 UdmaMode; | |
IdentifyData = AtaDevice->IdentifyData; | |
if ((IdentifyData->config & BIT15) != 0) { | |
// | |
// This is not an hard disk | |
// | |
return EFI_UNSUPPORTED; | |
} | |
DEBUG ((DEBUG_INFO, "AtaBus - Identify Device: Port %x PortMultiplierPort %x\n", AtaDevice->Port, AtaDevice->PortMultiplierPort)); | |
// | |
// Check whether the WORD 88 (supported UltraDMA by drive) is valid | |
// | |
if ((IdentifyData->field_validity & BIT2) != 0) { | |
UdmaMode = IdentifyData->ultra_dma_mode; | |
if ((UdmaMode & (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6)) != 0) { | |
// | |
// If BIT0~BIT6 is selected, then UDMA is supported | |
// | |
AtaDevice->UdmaValid = TRUE; | |
} | |
} | |
Capacity = GetAtapi6Capacity (AtaDevice); | |
if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) { | |
// | |
// Capacity exceeds 120GB. 48-bit addressing is really needed | |
// | |
AtaDevice->Lba48Bit = TRUE; | |
} else { | |
// | |
// This is a hard disk <= 120GB capacity, treat it as normal hard disk | |
// | |
Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) | IdentifyData->user_addressable_sectors_lo; | |
AtaDevice->Lba48Bit = FALSE; | |
} | |
// | |
// Block Media Information: | |
// | |
BlockMedia = &AtaDevice->BlockMedia; | |
BlockMedia->LastBlock = Capacity - 1; | |
BlockMedia->IoAlign = AtaDevice->AtaBusDriverData->AtaPassThru->Mode->IoAlign; | |
// | |
// Check whether Long Physical Sector Feature is supported | |
// | |
PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support; | |
if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) { | |
// | |
// Check whether one physical block contains multiple physical blocks | |
// | |
if ((PhyLogicSectorSupport & BIT13) != 0) { | |
BlockMedia->LogicalBlocksPerPhysicalBlock = (UINT32)(1 << (PhyLogicSectorSupport & 0x000f)); | |
// | |
// Check lowest alignment of logical blocks within physical block | |
// | |
if ((IdentifyData->alignment_logic_in_phy_blocks & (BIT14 | BIT15)) == BIT14) { | |
BlockMedia->LowestAlignedLba = (EFI_LBA)((BlockMedia->LogicalBlocksPerPhysicalBlock - ((UINT32)IdentifyData->alignment_logic_in_phy_blocks & 0x3fff)) % | |
BlockMedia->LogicalBlocksPerPhysicalBlock); | |
} | |
} | |
// | |
// Check logical block size | |
// | |
if ((PhyLogicSectorSupport & BIT12) != 0) { | |
BlockMedia->BlockSize = (UINT32)(((UINT32)(IdentifyData->logic_sector_size_hi << 16) | IdentifyData->logic_sector_size_lo) * sizeof (UINT16)); | |
} | |
AtaDevice->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2; | |
} | |
// | |
// Get ATA model name from identify data structure. | |
// | |
PrintAtaModelName (AtaDevice); | |
return EFI_SUCCESS; | |
} | |
/** | |
Discovers whether it is a valid ATA device. | |
This function issues ATA_CMD_IDENTIFY_DRIVE command to the ATA device to identify it. | |
If the command is executed successfully, it then identifies it and initializes | |
the Media information in Block IO protocol interface. | |
@param AtaDevice The ATA child device involved for the operation. | |
@retval EFI_SUCCESS The device is successfully identified and Media information | |
is correctly initialized. | |
@return others Some error occurs when discovering the ATA device. | |
**/ | |
EFI_STATUS | |
DiscoverAtaDevice ( | |
IN OUT ATA_DEVICE *AtaDevice | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_ATA_COMMAND_BLOCK *Acb; | |
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet; | |
UINTN Retry; | |
// | |
// Prepare for ATA command block. | |
// | |
Acb = ZeroMem (&AtaDevice->Acb, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
Acb->AtaCommand = ATA_CMD_IDENTIFY_DRIVE; | |
Acb->AtaDeviceHead = (UINT8)(BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort == 0xFFFF ? 0 : (AtaDevice->PortMultiplierPort << 4))); | |
// | |
// Prepare for ATA pass through packet. | |
// | |
Packet = ZeroMem (&AtaDevice->Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET)); | |
Packet->InDataBuffer = AtaDevice->IdentifyData; | |
Packet->InTransferLength = sizeof (ATA_IDENTIFY_DATA); | |
Packet->Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN; | |
Packet->Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT; | |
Packet->Timeout = ATA_TIMEOUT; | |
Retry = MAX_RETRY_TIMES; | |
do { | |
Status = AtaDevicePassThru (AtaDevice, NULL, NULL); | |
if (!EFI_ERROR (Status)) { | |
// | |
// The command is issued successfully | |
// | |
Status = IdentifyAtaDevice (AtaDevice); | |
return Status; | |
} | |
} while (Retry-- > 0); | |
return Status; | |
} | |
/** | |
Transfer data from ATA device. | |
This function performs one ATA pass through transaction to transfer data from/to | |
ATA device. It chooses the appropriate ATA command and protocol to invoke PassThru | |
interface of ATA pass through. | |
@param[in, out] AtaDevice The ATA child device involved for the operation. | |
@param[in, out] TaskPacket Pointer to a Pass Thru Command Packet. Optional, | |
if it is NULL, blocking mode, and use the packet | |
in AtaDevice. If it is not NULL, non blocking mode, | |
and pass down this Packet. | |
@param[in, out] Buffer The pointer to the current transaction buffer. | |
@param[in] StartLba The starting logical block address to be accessed. | |
@param[in] TransferLength The block number or sector count of the transfer. | |
@param[in] IsWrite Indicates whether it is a write operation. | |
@param[in] Event If Event is NULL, then blocking I/O is performed. | |
If Event is not NULL and non-blocking I/O is | |
supported,then non-blocking I/O is performed, | |
and Event will be signaled when the write | |
request is completed. | |
@retval EFI_SUCCESS The data transfer is complete successfully. | |
@return others Some error occurs when transferring data. | |
**/ | |
EFI_STATUS | |
TransferAtaDevice ( | |
IN OUT ATA_DEVICE *AtaDevice, | |
IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *TaskPacket OPTIONAL, | |
IN OUT VOID *Buffer, | |
IN EFI_LBA StartLba, | |
IN UINT32 TransferLength, | |
IN BOOLEAN IsWrite, | |
IN EFI_EVENT Event OPTIONAL | |
) | |
{ | |
EFI_ATA_COMMAND_BLOCK *Acb; | |
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet; | |
// | |
// Ensure AtaDevice->UdmaValid, AtaDevice->Lba48Bit and IsWrite are valid boolean values | |
// | |
ASSERT ((UINTN)AtaDevice->UdmaValid < 2); | |
ASSERT ((UINTN)AtaDevice->Lba48Bit < 2); | |
ASSERT ((UINTN)IsWrite < 2); | |
// | |
// Prepare for ATA command block. | |
// | |
Acb = ZeroMem (&AtaDevice->Acb, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
Acb->AtaCommand = mAtaCommands[AtaDevice->UdmaValid][AtaDevice->Lba48Bit][IsWrite]; | |
Acb->AtaSectorNumber = (UINT8)StartLba; | |
Acb->AtaCylinderLow = (UINT8)RShiftU64 (StartLba, 8); | |
Acb->AtaCylinderHigh = (UINT8)RShiftU64 (StartLba, 16); | |
Acb->AtaDeviceHead = (UINT8)(BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort == 0xFFFF ? 0 : (AtaDevice->PortMultiplierPort << 4))); | |
Acb->AtaSectorCount = (UINT8)TransferLength; | |
if (AtaDevice->Lba48Bit) { | |
Acb->AtaSectorNumberExp = (UINT8)RShiftU64 (StartLba, 24); | |
Acb->AtaCylinderLowExp = (UINT8)RShiftU64 (StartLba, 32); | |
Acb->AtaCylinderHighExp = (UINT8)RShiftU64 (StartLba, 40); | |
Acb->AtaSectorCountExp = (UINT8)(TransferLength >> 8); | |
} else { | |
Acb->AtaDeviceHead = (UINT8)(Acb->AtaDeviceHead | RShiftU64 (StartLba, 24)); | |
} | |
// | |
// Prepare for ATA pass through packet. | |
// | |
if (TaskPacket != NULL) { | |
Packet = ZeroMem (TaskPacket, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET)); | |
} else { | |
Packet = ZeroMem (&AtaDevice->Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET)); | |
} | |
if (IsWrite) { | |
Packet->OutDataBuffer = Buffer; | |
Packet->OutTransferLength = TransferLength; | |
} else { | |
Packet->InDataBuffer = Buffer; | |
Packet->InTransferLength = TransferLength; | |
} | |
Packet->Protocol = mAtaPassThruCmdProtocols[AtaDevice->UdmaValid][IsWrite]; | |
Packet->Length = EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT; | |
// | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | ATA PIO Transfer Mode | Transfer Rate | ATA DMA Transfer Mode | Transfer Rate | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | PIO Mode 0 | 3.3Mbytes/sec | Single-word DMA Mode 0 | 2.1Mbytes/sec | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | PIO Mode 1 | 5.2Mbytes/sec | Single-word DMA Mode 1 | 4.2Mbytes/sec | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | PIO Mode 2 | 8.3Mbytes/sec | Single-word DMA Mode 2 | 8.4Mbytes/sec | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | PIO Mode 3 | 11.1Mbytes/sec | Multi-word DMA Mode 0 | 4.2Mbytes/sec | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | PIO Mode 4 | 16.6Mbytes/sec | Multi-word DMA Mode 1 | 13.3Mbytes/sec | | |
// |------------------------|-----------------|------------------------|-----------------| | |
// | |
// As AtaBus is used to manage ATA devices, we have to use the lowest transfer rate to | |
// calculate the possible maximum timeout value for each read/write operation. | |
// The timeout value is rounded up to nearest integer and here an additional 30s is added | |
// to follow ATA spec in which it mentioned that the device may take up to 30s to respond | |
// commands in the Standby/Idle mode. | |
// | |
if (AtaDevice->UdmaValid) { | |
// | |
// Calculate the maximum timeout value for DMA read/write operation. | |
// | |
Packet->Timeout = EFI_TIMER_PERIOD_SECONDS (DivU64x32 (MultU64x32 (TransferLength, AtaDevice->BlockMedia.BlockSize), 2100000) + 31); | |
} else { | |
// | |
// Calculate the maximum timeout value for PIO read/write operation | |
// | |
Packet->Timeout = EFI_TIMER_PERIOD_SECONDS (DivU64x32 (MultU64x32 (TransferLength, AtaDevice->BlockMedia.BlockSize), 3300000) + 31); | |
} | |
return AtaDevicePassThru (AtaDevice, TaskPacket, Event); | |
} | |
/** | |
Free SubTask. | |
@param[in, out] Task Pointer to task to be freed. | |
**/ | |
VOID | |
EFIAPI | |
FreeAtaSubTask ( | |
IN OUT ATA_BUS_ASYN_SUB_TASK *Task | |
) | |
{ | |
if (Task->Packet.Asb != NULL) { | |
FreeAlignedBuffer (Task->Packet.Asb, sizeof (EFI_ATA_STATUS_BLOCK)); | |
} | |
if (Task->Packet.Acb != NULL) { | |
FreePool (Task->Packet.Acb); | |
} | |
FreePool (Task); | |
} | |
/** | |
Terminate any in-flight non-blocking I/O requests by signaling an EFI_ABORTED | |
in the TransactionStatus member of the EFI_BLOCK_IO2_TOKEN for the non-blocking | |
I/O. After that it is safe to free any Token or Buffer data structures that | |
were allocated to initiate the non-blockingI/O requests that were in-flight for | |
this device. | |
@param[in] AtaDevice The ATA child device involved for the operation. | |
**/ | |
VOID | |
EFIAPI | |
AtaTerminateNonBlockingTask ( | |
IN ATA_DEVICE *AtaDevice | |
) | |
{ | |
BOOLEAN SubTaskEmpty; | |
EFI_TPL OldTpl; | |
ATA_BUS_ASYN_TASK *AtaTask; | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *List; | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
// | |
// Abort all executing tasks from now. | |
// | |
AtaDevice->Abort = TRUE; | |
List = &AtaDevice->AtaTaskList; | |
for (Entry = GetFirstNode (List); !IsNull (List, Entry);) { | |
AtaTask = ATA_ASYN_TASK_FROM_ENTRY (Entry); | |
AtaTask->Token->TransactionStatus = EFI_ABORTED; | |
gBS->SignalEvent (AtaTask->Token->Event); | |
Entry = RemoveEntryList (Entry); | |
FreePool (AtaTask); | |
} | |
gBS->RestoreTPL (OldTpl); | |
do { | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
// | |
// Wait for executing subtasks done. | |
// | |
SubTaskEmpty = IsListEmpty (&AtaDevice->AtaSubTaskList); | |
gBS->RestoreTPL (OldTpl); | |
} while (!SubTaskEmpty); | |
// | |
// Aborting operation has been done. From now on, don't need to abort normal operation. | |
// | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
AtaDevice->Abort = FALSE; | |
gBS->RestoreTPL (OldTpl); | |
} | |
/** | |
Call back function when the event is signaled. | |
@param[in] Event The Event this notify function registered to. | |
@param[in] Context Pointer to the context data registered to the | |
Event. | |
**/ | |
VOID | |
EFIAPI | |
AtaNonBlockingCallBack ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
ATA_BUS_ASYN_SUB_TASK *Task; | |
ATA_BUS_ASYN_TASK *AtaTask; | |
ATA_DEVICE *AtaDevice; | |
LIST_ENTRY *Entry; | |
EFI_STATUS Status; | |
Task = (ATA_BUS_ASYN_SUB_TASK *)Context; | |
gBS->CloseEvent (Event); | |
AtaDevice = Task->AtaDevice; | |
// | |
// Check the command status. | |
// If there is error during the sub task source allocation, the error status | |
// should be returned to the caller directly, so here the Task->Token may already | |
// be deleted by the caller and no need to update the status. | |
// | |
if ((!(*Task->IsError)) && ((Task->Packet.Asb->AtaStatus & 0x01) == 0x01)) { | |
Task->Token->TransactionStatus = EFI_DEVICE_ERROR; | |
} | |
if (AtaDevice->Abort) { | |
Task->Token->TransactionStatus = EFI_ABORTED; | |
} | |
DEBUG (( | |
DEBUG_BLKIO, | |
"NON-BLOCKING EVENT FINISHED!- STATUS = %r\n", | |
Task->Token->TransactionStatus | |
)); | |
// | |
// Reduce the SubEventCount, till it comes to zero. | |
// | |
(*Task->UnsignalledEventCount)--; | |
DEBUG ((DEBUG_BLKIO, "UnsignalledEventCount = %d\n", *Task->UnsignalledEventCount)); | |
// | |
// Remove the SubTask from the Task list. | |
// | |
RemoveEntryList (&Task->TaskEntry); | |
if ((*Task->UnsignalledEventCount) == 0) { | |
// | |
// All Sub tasks are done, then signal the upper layer event. | |
// Except there is error during the sub task source allocation. | |
// | |
if (!(*Task->IsError)) { | |
gBS->SignalEvent (Task->Token->Event); | |
DEBUG ((DEBUG_BLKIO, "Signal the upper layer event!\n")); | |
} | |
FreePool (Task->UnsignalledEventCount); | |
FreePool (Task->IsError); | |
// | |
// Finish all subtasks and move to the next task in AtaTaskList. | |
// | |
if (!IsListEmpty (&AtaDevice->AtaTaskList)) { | |
Entry = GetFirstNode (&AtaDevice->AtaTaskList); | |
AtaTask = ATA_ASYN_TASK_FROM_ENTRY (Entry); | |
DEBUG ((DEBUG_BLKIO, "Start to embark a new Ata Task\n")); | |
DEBUG ((DEBUG_BLKIO, "AtaTask->NumberOfBlocks = %x; AtaTask->Token=%x\n", AtaTask->NumberOfBlocks, AtaTask->Token)); | |
Status = AccessAtaDevice ( | |
AtaTask->AtaDevice, | |
AtaTask->Buffer, | |
AtaTask->StartLba, | |
AtaTask->NumberOfBlocks, | |
AtaTask->IsWrite, | |
AtaTask->Token | |
); | |
if (EFI_ERROR (Status)) { | |
AtaTask->Token->TransactionStatus = Status; | |
gBS->SignalEvent (AtaTask->Token->Event); | |
} | |
RemoveEntryList (Entry); | |
FreePool (AtaTask); | |
} | |
} | |
DEBUG (( | |
DEBUG_BLKIO, | |
"PACKET INFO: Write=%s, Length=%x, LowCylinder=%x, HighCylinder=%x, SectionNumber=%x\n", | |
Task->Packet.OutDataBuffer != NULL ? L"YES" : L"NO", | |
Task->Packet.OutDataBuffer != NULL ? Task->Packet.OutTransferLength : Task->Packet.InTransferLength, | |
Task->Packet.Acb->AtaCylinderLow, | |
Task->Packet.Acb->AtaCylinderHigh, | |
Task->Packet.Acb->AtaSectorCount | |
)); | |
// | |
// Free the buffer of SubTask. | |
// | |
FreeAtaSubTask (Task); | |
} | |
/** | |
Read or write a number of blocks from ATA device. | |
This function performs ATA pass through transactions to read/write data from/to | |
ATA device. It may separate the read/write request into several ATA pass through | |
transactions. | |
@param[in, out] AtaDevice The ATA child device involved for the operation. | |
@param[in, out] Buffer The pointer to the current transaction buffer. | |
@param[in] StartLba The starting logical block address to be accessed. | |
@param[in] NumberOfBlocks The block number or sector count of the transfer. | |
@param[in] IsWrite Indicates whether it is a write operation. | |
@param[in, out] Token A pointer to the token associated with the transaction. | |
@retval EFI_SUCCESS The data transfer is complete successfully. | |
@return others Some error occurs when transferring data. | |
**/ | |
EFI_STATUS | |
AccessAtaDevice ( | |
IN OUT ATA_DEVICE *AtaDevice, | |
IN OUT UINT8 *Buffer, | |
IN EFI_LBA StartLba, | |
IN UINTN NumberOfBlocks, | |
IN BOOLEAN IsWrite, | |
IN OUT EFI_BLOCK_IO2_TOKEN *Token | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN MaxTransferBlockNumber; | |
UINTN TransferBlockNumber; | |
UINTN BlockSize; | |
ATA_BUS_ASYN_SUB_TASK *SubTask; | |
UINTN *EventCount; | |
UINTN TempCount; | |
ATA_BUS_ASYN_TASK *AtaTask; | |
EFI_EVENT SubEvent; | |
UINTN Index; | |
BOOLEAN *IsError; | |
EFI_TPL OldTpl; | |
TempCount = 0; | |
Status = EFI_SUCCESS; | |
EventCount = NULL; | |
IsError = NULL; | |
Index = 0; | |
SubTask = NULL; | |
SubEvent = NULL; | |
AtaTask = NULL; | |
// | |
// Ensure AtaDevice->Lba48Bit is a valid boolean value | |
// | |
ASSERT ((UINTN)AtaDevice->Lba48Bit < 2); | |
MaxTransferBlockNumber = mMaxTransferBlockNumber[AtaDevice->Lba48Bit]; | |
BlockSize = AtaDevice->BlockMedia.BlockSize; | |
// | |
// Initial the return status and shared account for Non Blocking. | |
// | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
if (!IsListEmpty (&AtaDevice->AtaSubTaskList)) { | |
AtaTask = AllocateZeroPool (sizeof (ATA_BUS_ASYN_TASK)); | |
if (AtaTask == NULL) { | |
gBS->RestoreTPL (OldTpl); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
AtaTask->AtaDevice = AtaDevice; | |
AtaTask->Buffer = Buffer; | |
AtaTask->IsWrite = IsWrite; | |
AtaTask->NumberOfBlocks = NumberOfBlocks; | |
AtaTask->Signature = ATA_TASK_SIGNATURE; | |
AtaTask->StartLba = StartLba; | |
AtaTask->Token = Token; | |
InsertTailList (&AtaDevice->AtaTaskList, &AtaTask->TaskEntry); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
gBS->RestoreTPL (OldTpl); | |
Token->TransactionStatus = EFI_SUCCESS; | |
EventCount = AllocateZeroPool (sizeof (UINTN)); | |
if (EventCount == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
IsError = AllocateZeroPool (sizeof (BOOLEAN)); | |
if (IsError == NULL) { | |
FreePool (EventCount); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DEBUG ((DEBUG_BLKIO, "Allocation IsError Addr=%x\n", IsError)); | |
*IsError = FALSE; | |
TempCount = (NumberOfBlocks + MaxTransferBlockNumber - 1) / MaxTransferBlockNumber; | |
*EventCount = TempCount; | |
DEBUG ((DEBUG_BLKIO, "AccessAtaDevice, NumberOfBlocks=%x\n", NumberOfBlocks)); | |
DEBUG ((DEBUG_BLKIO, "AccessAtaDevice, MaxTransferBlockNumber=%x\n", MaxTransferBlockNumber)); | |
DEBUG ((DEBUG_BLKIO, "AccessAtaDevice, EventCount=%x\n", TempCount)); | |
} else { | |
while (!IsListEmpty (&AtaDevice->AtaTaskList) || !IsListEmpty (&AtaDevice->AtaSubTaskList)) { | |
// | |
// Stall for 100us. | |
// | |
MicroSecondDelay (100); | |
} | |
} | |
do { | |
if (NumberOfBlocks > MaxTransferBlockNumber) { | |
TransferBlockNumber = MaxTransferBlockNumber; | |
NumberOfBlocks -= MaxTransferBlockNumber; | |
} else { | |
TransferBlockNumber = NumberOfBlocks; | |
NumberOfBlocks = 0; | |
} | |
// | |
// Create sub event for the sub ata task. Non-blocking mode. | |
// | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
SubTask = NULL; | |
SubEvent = NULL; | |
SubTask = AllocateZeroPool (sizeof (ATA_BUS_ASYN_SUB_TASK)); | |
if (SubTask == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto EXIT; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
SubTask->UnsignalledEventCount = EventCount; | |
SubTask->Signature = ATA_SUB_TASK_SIGNATURE; | |
SubTask->AtaDevice = AtaDevice; | |
SubTask->Token = Token; | |
SubTask->IsError = IsError; | |
InsertTailList (&AtaDevice->AtaSubTaskList, &SubTask->TaskEntry); | |
gBS->RestoreTPL (OldTpl); | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
AtaNonBlockingCallBack, | |
SubTask, | |
&SubEvent | |
); | |
// | |
// If resource allocation fail, the un-signalled event count should equal to | |
// the original one minus the unassigned subtasks number. | |
// | |
if (EFI_ERROR (Status)) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto EXIT; | |
} | |
Status = TransferAtaDevice (AtaDevice, &SubTask->Packet, Buffer, StartLba, (UINT32)TransferBlockNumber, IsWrite, SubEvent); | |
} else { | |
// | |
// Blocking Mode. | |
// | |
DEBUG ((DEBUG_BLKIO, "Blocking AccessAtaDevice, TransferBlockNumber=%x; StartLba = %x\n", TransferBlockNumber, StartLba)); | |
Status = TransferAtaDevice (AtaDevice, NULL, Buffer, StartLba, (UINT32)TransferBlockNumber, IsWrite, NULL); | |
} | |
if (EFI_ERROR (Status)) { | |
goto EXIT; | |
} | |
Index++; | |
StartLba += TransferBlockNumber; | |
Buffer += TransferBlockNumber * BlockSize; | |
} while (NumberOfBlocks > 0); | |
EXIT: | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
// | |
// Release resource at non-blocking mode. | |
// | |
if (EFI_ERROR (Status)) { | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
Token->TransactionStatus = Status; | |
*EventCount = (*EventCount) - (TempCount - Index); | |
*IsError = TRUE; | |
if (*EventCount == 0) { | |
FreePool (EventCount); | |
FreePool (IsError); | |
} | |
if (SubTask != NULL) { | |
RemoveEntryList (&SubTask->TaskEntry); | |
FreeAtaSubTask (SubTask); | |
} | |
if (SubEvent != NULL) { | |
gBS->CloseEvent (SubEvent); | |
} | |
gBS->RestoreTPL (OldTpl); | |
} | |
} | |
return Status; | |
} | |
/** | |
Trust transfer data from/to ATA device. | |
This function performs one ATA pass through transaction to do a trust transfer from/to | |
ATA device. It chooses the appropriate ATA command and protocol to invoke PassThru | |
interface of ATA pass through. | |
@param AtaDevice The ATA child device involved for the operation. | |
@param Buffer The pointer to the current transaction buffer. | |
@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 TransferLength The block number or sector count of the transfer. | |
@param IsTrustSend Indicates whether it is a trust send operation or not. | |
@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 TransferLengthOut A pointer to a buffer to store the size in bytes of the data | |
written to the buffer. Ignore it when IsTrustSend is TRUE. | |
@retval EFI_SUCCESS The data transfer is complete successfully. | |
@return others Some error occurs when transferring data. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
TrustTransferAtaDevice ( | |
IN OUT ATA_DEVICE *AtaDevice, | |
IN OUT VOID *Buffer, | |
IN UINT8 SecurityProtocolId, | |
IN UINT16 SecurityProtocolSpecificData, | |
IN UINTN TransferLength, | |
IN BOOLEAN IsTrustSend, | |
IN UINT64 Timeout, | |
OUT UINTN *TransferLengthOut | |
) | |
{ | |
EFI_ATA_COMMAND_BLOCK *Acb; | |
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet; | |
EFI_STATUS Status; | |
VOID *NewBuffer; | |
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; | |
// | |
// Ensure AtaDevice->UdmaValid and IsTrustSend are valid boolean values | |
// | |
ASSERT ((UINTN)AtaDevice->UdmaValid < 2); | |
ASSERT ((UINTN)IsTrustSend < 2); | |
// | |
// Prepare for ATA command block. | |
// | |
Acb = ZeroMem (&AtaDevice->Acb, sizeof (EFI_ATA_COMMAND_BLOCK)); | |
if (TransferLength == 0) { | |
Acb->AtaCommand = ATA_CMD_TRUST_NON_DATA; | |
} else { | |
Acb->AtaCommand = mAtaTrustCommands[AtaDevice->UdmaValid][IsTrustSend]; | |
} | |
Acb->AtaFeatures = SecurityProtocolId; | |
Acb->AtaSectorCount = (UINT8)(TransferLength / 512); | |
Acb->AtaSectorNumber = (UINT8)((TransferLength / 512) >> 8); | |
// | |
// NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout. | |
// Here use big endian for Cylinder register. | |
// | |
Acb->AtaCylinderHigh = (UINT8)SecurityProtocolSpecificData; | |
Acb->AtaCylinderLow = (UINT8)(SecurityProtocolSpecificData >> 8); | |
Acb->AtaDeviceHead = (UINT8)(BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort == 0xFFFF ? 0 : (AtaDevice->PortMultiplierPort << 4))); | |
// | |
// Prepare for ATA pass through packet. | |
// | |
Packet = ZeroMem (&AtaDevice->Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET)); | |
if (TransferLength == 0) { | |
Packet->InTransferLength = 0; | |
Packet->OutTransferLength = 0; | |
Packet->Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA; | |
} else if (IsTrustSend) { | |
// | |
// Check the alignment of the incoming buffer prior to invoking underlying ATA PassThru | |
// | |
AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru; | |
if ((AtaPassThru->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) { | |
NewBuffer = AllocateAlignedBuffer (AtaDevice, TransferLength); | |
if (NewBuffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CopyMem (NewBuffer, Buffer, TransferLength); | |
FreePool (Buffer); | |
Buffer = NewBuffer; | |
} | |
Packet->OutDataBuffer = Buffer; | |
Packet->OutTransferLength = (UINT32)TransferLength; | |
Packet->Protocol = mAtaPassThruCmdProtocols[AtaDevice->UdmaValid][IsTrustSend]; | |
} else { | |
Packet->InDataBuffer = Buffer; | |
Packet->InTransferLength = (UINT32)TransferLength; | |
Packet->Protocol = mAtaPassThruCmdProtocols[AtaDevice->UdmaValid][IsTrustSend]; | |
} | |
Packet->Length = EFI_ATA_PASS_THRU_LENGTH_BYTES; | |
Packet->Timeout = Timeout; | |
Status = AtaDevicePassThru (AtaDevice, NULL, NULL); | |
if (TransferLengthOut != NULL) { | |
if (!IsTrustSend) { | |
*TransferLengthOut = Packet->InTransferLength; | |
} | |
} | |
return Status; | |
} |