| /** @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. | |
| Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "AtaBus.h" | |
| // | |
| // 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 (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 AtaDevice The ATA child device involved for the operation. | |
| @return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru(). | |
| **/ | |
| EFI_STATUS | |
| AtaDevicePassThru ( | |
| IN OUT ATA_DEVICE *AtaDevice | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; | |
| EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet; | |
| // | |
| // Assemble packet | |
| // | |
| Packet = &AtaDevice->Packet; | |
| Packet->Asb = AtaDevice->Asb; | |
| Packet->Acb = &AtaDevice->Acb; | |
| Packet->Timeout = ATA_TIMEOUT; | |
| AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru; | |
| Status = AtaPassThru->PassThru ( | |
| AtaPassThru, | |
| AtaDevice->Port, | |
| AtaDevice->PortMultiplierPort, | |
| Packet, | |
| NULL | |
| ); | |
| // | |
| // 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; | |
| 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; | |
| } | |
| // | |
| // 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) (((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 (*Acb)); | |
| Acb->AtaCommand = ATA_CMD_IDENTIFY_DRIVE; | |
| Acb->AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort << 4)); | |
| // | |
| // Prepare for ATA pass through packet. | |
| // | |
| Packet = ZeroMem (&AtaDevice->Packet, sizeof (*Packet)); | |
| Packet->InDataBuffer = AtaDevice->IdentifyData; | |
| Packet->InTransferLength = sizeof (*AtaDevice->IdentifyData); | |
| 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; | |
| Retry = MAX_RETRY_TIMES; | |
| do { | |
| Status = AtaDevicePassThru (AtaDevice); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // The command is issued successfully | |
| // | |
| Status = IdentifyAtaDevice (AtaDevice); | |
| if (!EFI_ERROR (Status)) { | |
| 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 AtaDevice The ATA child device involved for the operation. | |
| @param Buffer The pointer to the current transaction buffer. | |
| @param StartLba The starting logical block address to be accessed. | |
| @param TransferLength The block number or sector count of the transfer. | |
| @param IsWrite Indicates whether it is a write operation. | |
| @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 VOID *Buffer, | |
| IN EFI_LBA StartLba, | |
| IN UINT32 TransferLength, | |
| IN BOOLEAN IsWrite | |
| ) | |
| { | |
| 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 (*Acb)); | |
| 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 << 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. | |
| // | |
| Packet = ZeroMem (&AtaDevice->Packet, sizeof (*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; | |
| return AtaDevicePassThru (AtaDevice); | |
| } | |
| /** | |
| 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 AtaDevice The ATA child device involved for the operation. | |
| @param Buffer The pointer to the current transaction buffer. | |
| @param StartLba The starting logical block address to be accessed. | |
| @param NumberOfBlocks The block number or sector count of the transfer. | |
| @param IsWrite Indicates whether it is a write operation. | |
| @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 | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN MaxTransferBlockNumber; | |
| UINTN TransferBlockNumber; | |
| UINTN BlockSize; | |
| // | |
| // Ensure AtaDevice->Lba48Bit is a valid boolean value | |
| // | |
| ASSERT ((UINTN) AtaDevice->Lba48Bit < 2); | |
| MaxTransferBlockNumber = mMaxTransferBlockNumber[AtaDevice->Lba48Bit]; | |
| BlockSize = AtaDevice->BlockMedia.BlockSize; | |
| do { | |
| if (NumberOfBlocks > MaxTransferBlockNumber) { | |
| TransferBlockNumber = MaxTransferBlockNumber; | |
| NumberOfBlocks -= MaxTransferBlockNumber; | |
| } else { | |
| TransferBlockNumber = NumberOfBlocks; | |
| NumberOfBlocks = 0; | |
| } | |
| Status = TransferAtaDevice (AtaDevice, Buffer, StartLba, (UINT32) TransferBlockNumber, IsWrite); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| StartLba += TransferBlockNumber; | |
| Buffer += TransferBlockNumber * BlockSize; | |
| } while (NumberOfBlocks > 0); | |
| return Status; | |
| } |