230 lines
7.0 KiB
C
230 lines
7.0 KiB
C
/** @file
|
|
This file provides AHCI SATA device block access interfaces.
|
|
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "AhciDevice.h"
|
|
|
|
/**
|
|
Calculate max transfer sector count.
|
|
|
|
The function is designed to calculate disk max transfer sectpr count.
|
|
|
|
@param[in] AtaDevice AHCI SATA device information pointer.
|
|
|
|
@retval AHCI_MAX_48_TRANSFER_SECTOR Support 48bit LBA
|
|
@retval AHCI_MAX_28_TRANSFER_SECTOR Support 28bit LBA
|
|
|
|
**/
|
|
UINT32
|
|
GetMaxTransferSector (
|
|
IN EFI_ATA_DEVICE_INFO *AtaDevice
|
|
)
|
|
{
|
|
UINT32 BlkCnt;
|
|
|
|
if (AtaDevice->DeviceFeature & DEVICE_LBA_48_SUPPORT) {
|
|
if (FeaturePcdGet(PcdDmaProtectionEnabled)) {
|
|
// When DMA protection is enabled, only tranfer less than DMA buffer size
|
|
// Use half for safe. Around 1MB will be used for CmdTable.
|
|
BlkCnt = (PcdGet32 (PcdDmaBufferSize) >> 1) / AtaDevice->BlockSize;
|
|
if (BlkCnt == 0) {
|
|
BlkCnt = 1;
|
|
} else if (BlkCnt > AHCI_MAX_48_TRANSFER_SECTOR) {
|
|
BlkCnt = AHCI_MAX_48_TRANSFER_SECTOR;
|
|
}
|
|
} else {
|
|
BlkCnt = AHCI_MAX_48_TRANSFER_SECTOR;
|
|
}
|
|
} else {
|
|
BlkCnt = AHCI_MAX_28_TRANSFER_SECTOR;
|
|
}
|
|
return BlkCnt;
|
|
}
|
|
|
|
/**
|
|
Block read/write to ATA device.
|
|
|
|
The function performs read or write operation to the ATA device.
|
|
|
|
@param[in] AtaDevice ATA device instance.
|
|
@param[in] Read Read or write.
|
|
@param[in] StartLba The starting logical block address (LBA) to read from
|
|
on the device.
|
|
@param[in] SectorCount The sector count to read or write.
|
|
@param[in, out] MemoryAddr A pointer to the buffer for the data.
|
|
The caller is responsible for the ownership of the
|
|
buffer.
|
|
|
|
@retval EFI_DEVICE_ERROR The DMA 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 DMA data transfer executes successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AhciAtaDeviceReadWrite (
|
|
IN EFI_ATA_DEVICE_INFO *AtaDevice,
|
|
IN BOOLEAN Read,
|
|
IN EFI_LBA StartLba,
|
|
IN UINT32 SectorCount,
|
|
IN OUT VOID *MemoryAddr
|
|
)
|
|
{
|
|
EFI_AHCI_CONTROLLER *AhciController;
|
|
EFI_ATA_COMMAND_BLOCK AtaCmdBlk;
|
|
|
|
AhciController = AtaDevice->Controller;
|
|
|
|
//
|
|
// Prepare for ATA command block.
|
|
//
|
|
ZeroMem (&AtaCmdBlk, sizeof (EFI_ATA_COMMAND_BLOCK));
|
|
|
|
AtaCmdBlk.AtaCommand = (AtaDevice->DeviceFeature & DEVICE_LBA_48_SUPPORT) ? ATA_CMD_READ_DMA_EXT : ATA_CMD_READ_DMA;
|
|
AtaCmdBlk.AtaSectorNumber = (UINT8) StartLba;
|
|
AtaCmdBlk.AtaCylinderLow = (UINT8) RShiftU64 (StartLba, 8);
|
|
AtaCmdBlk.AtaCylinderHigh = (UINT8) RShiftU64 (StartLba, 16);
|
|
AtaCmdBlk.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | \
|
|
(AtaDevice->PortMultiplier == 0xFFFF ? 0 : (AtaDevice->PortMultiplier << 4)));
|
|
AtaCmdBlk.AtaSectorCount = (UINT8) SectorCount;
|
|
|
|
if (AtaDevice->DeviceFeature & DEVICE_LBA_48_SUPPORT) {
|
|
AtaCmdBlk.AtaSectorNumberExp = (UINT8) RShiftU64 (StartLba, 24);
|
|
AtaCmdBlk.AtaCylinderLowExp = (UINT8) RShiftU64 (StartLba, 32);
|
|
AtaCmdBlk.AtaCylinderHighExp = (UINT8) RShiftU64 (StartLba, 40);
|
|
AtaCmdBlk.AtaSectorCountExp = (UINT8) (SectorCount >> 8);
|
|
} else {
|
|
AtaCmdBlk.AtaDeviceHead = (UINT8) (AtaCmdBlk.AtaDeviceHead | RShiftU64 (StartLba, 24));
|
|
}
|
|
|
|
return AhciDmaTransfer (
|
|
AhciController,
|
|
&AhciController->AhciRegisters,
|
|
(UINT8)AtaDevice->Port,
|
|
(UINT8)AtaDevice->PortMultiplier,
|
|
NULL,
|
|
0,
|
|
Read,
|
|
&AtaCmdBlk,
|
|
NULL,
|
|
MemoryAddr,
|
|
SectorCount * AtaDevice->BlockSize,
|
|
DMA_WAIT_TIMEOUT_MS * 1000 * 10
|
|
);
|
|
}
|
|
|
|
/**
|
|
Block read/write to ATA device.
|
|
|
|
The function performs read or write operation to the ATA device.
|
|
|
|
@param[in] AtaDevice ATA device instance.
|
|
@param[in] Read Read or write.
|
|
@param[in] Lba The starting logical block address (LBA) to read from
|
|
on the device.
|
|
@param[in] BufferSize The size of the Buffer in bytes. This number must be
|
|
a multiple of the intrinsic block size of the device.
|
|
@param[out] Buffer A pointer to the destination buffer for the data.
|
|
The caller is responsible for the ownership of the
|
|
buffer.
|
|
|
|
@retval EFI_INVALID_PARAMETER Inpurt parameters are not valid.
|
|
@retval EFI_DEVICE_ERROR The DMA 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 DMA data transfer executes successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AhciReadWriteBlock (
|
|
IN EFI_ATA_DEVICE_INFO *AtaDevice,
|
|
IN BOOLEAN Read,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LBA LbaIndex;
|
|
UINT8 *ReadBuf;
|
|
UINTN BlockSize;
|
|
UINTN NumberOfBlocks;
|
|
UINT32 IoAlign;
|
|
UINT32 MaxTransferSector;
|
|
UINT32 RemainSectorCount;
|
|
|
|
if (AtaDevice == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ReadBuf = Buffer;
|
|
LbaIndex = Lba;
|
|
|
|
if (AtaDevice == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Get the intrinsic block size
|
|
//
|
|
BlockSize = AtaDevice->BlockSize;
|
|
NumberOfBlocks = BufferSize / BlockSize;
|
|
|
|
|
|
if (BufferSize % BlockSize != 0) {
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
if (Lba >= AtaDevice->TotalBlockNumber) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Lba + NumberOfBlocks - 1) >= AtaDevice->TotalBlockNumber) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
IoAlign = 2;
|
|
if ((IoAlign > 1) && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
MaxTransferSector = GetMaxTransferSector (AtaDevice);
|
|
RemainSectorCount = (UINT32)NumberOfBlocks;
|
|
while (RemainSectorCount != 0) {
|
|
Status = AhciAtaDeviceReadWrite (
|
|
AtaDevice,
|
|
Read,
|
|
LbaIndex,
|
|
RemainSectorCount >= MaxTransferSector ? MaxTransferSector : RemainSectorCount,
|
|
ReadBuf
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "AhciDeviceRead Status = %r\n", Status));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (RemainSectorCount >= MaxTransferSector) {
|
|
ReadBuf += (UINTN) (AtaDevice->BlockSize * MaxTransferSector);
|
|
LbaIndex += MaxTransferSector;
|
|
RemainSectorCount -= MaxTransferSector;
|
|
} else {
|
|
RemainSectorCount = 0;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|