From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Bus/Ata/AtaAtapiPassThru/IdeMode.c | 2661 ++++++++++++++++++++ 1 file changed, 2661 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c (limited to 'roms/edk2/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c') diff --git a/roms/edk2/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c b/roms/edk2/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c new file mode 100644 index 000000000..d284cc600 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c @@ -0,0 +1,2661 @@ +/** @file + Header file for AHCI mode of ATA host controller. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AtaAtapiPassThru.h" + +/** + read a one-byte data from a IDE port. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port The IDE Port number + + @return the one-byte data read from IDE port +**/ +UINT8 +EFIAPI +IdeReadPortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port + ) +{ + UINT8 Data; + + ASSERT (PciIo != NULL); + + Data = 0; + // + // perform 1-byte data read from register + // + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); + return Data; +} + +/** + write a 1-byte data to a specific IDE port. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port The IDE port to be written + @param Data The data to write to the port +**/ +VOID +EFIAPI +IdeWritePortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT8 Data + ) +{ + ASSERT (PciIo != NULL); + + // + // perform 1-byte data write to register + // + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); +} + +/** + write a 1-word data to a specific IDE port. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port The IDE port to be written + @param Data The data to write to the port +**/ +VOID +EFIAPI +IdeWritePortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT16 Data + ) +{ + ASSERT (PciIo != NULL); + + // + // perform 1-word data write to register + // + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); +} + +/** + write a 2-word data to a specific IDE port. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port The IDE port to be written + @param Data The data to write to the port +**/ +VOID +EFIAPI +IdeWritePortDW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT32 Data + ) +{ + ASSERT (PciIo != NULL); + + // + // perform 2-word data write to register + // + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); +} + +/** + Write multiple words of data to the IDE data port. + Call the IO abstraction once to do the complete read, + not one word at a time + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port IO port to read + @param Count No. of UINT16's to read + @param Buffer Pointer to the data buffer for read + +**/ +VOID +EFIAPI +IdeWritePortWMultiple ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINTN Count, + IN VOID *Buffer + ) +{ + ASSERT (PciIo != NULL); + ASSERT (Buffer != NULL); + + // + // perform UINT16 data write to the FIFO + // + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthFifoUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + Count, + (UINT16 *) Buffer + ); + +} + +/** + Reads multiple words of data from the IDE data port. + Call the IO abstraction once to do the complete read, + not one word at a time + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure + @param Port IO port to read + @param Count Number of UINT16's to read + @param Buffer Pointer to the data buffer for read + +**/ +VOID +EFIAPI +IdeReadPortWMultiple ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINTN Count, + IN VOID *Buffer + ) +{ + ASSERT (PciIo != NULL); + ASSERT (Buffer != NULL); + + // + // Perform UINT16 data read from FIFO + // + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthFifoUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + Count, + (UINT16 *) Buffer + ); + +} + +/** + This function is used to analyze the Status Register and print out + some debug information and if there is ERR bit set in the Status + Register, the Error Register's value is also be parsed and print out. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + +**/ +VOID +EFIAPI +DumpAllIdeRegisters ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_ATA_STATUS_BLOCK StatusBlock; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + ZeroMem (&StatusBlock, sizeof (EFI_ATA_STATUS_BLOCK)); + + StatusBlock.AtaStatus = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); + StatusBlock.AtaError = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); + StatusBlock.AtaSectorCount = IdeReadPortB (PciIo, IdeRegisters->SectorCount); + StatusBlock.AtaSectorCountExp = IdeReadPortB (PciIo, IdeRegisters->SectorCount); + StatusBlock.AtaSectorNumber = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); + StatusBlock.AtaSectorNumberExp = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); + StatusBlock.AtaCylinderLow = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); + StatusBlock.AtaCylinderLowExp = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); + StatusBlock.AtaCylinderHigh = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); + StatusBlock.AtaCylinderHighExp = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); + StatusBlock.AtaDeviceHead = IdeReadPortB (PciIo, IdeRegisters->Head); + + if (AtaStatusBlock != NULL) { + // + // Dump the content of all ATA registers. + // + CopyMem (AtaStatusBlock, &StatusBlock, sizeof (EFI_ATA_STATUS_BLOCK)); + } + + DEBUG_CODE_BEGIN (); + if ((StatusBlock.AtaStatus & ATA_STSREG_DWF) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Write Fault\n", StatusBlock.AtaStatus)); + } + + if ((StatusBlock.AtaStatus & ATA_STSREG_CORR) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Corrected Data\n", StatusBlock.AtaStatus)); + } + + if ((StatusBlock.AtaStatus & ATA_STSREG_ERR) != 0) { + if ((StatusBlock.AtaError & ATA_ERRREG_BBK) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Bad Block Detected\n", StatusBlock.AtaError)); + } + + if ((StatusBlock.AtaError & ATA_ERRREG_UNC) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Uncorrectable Data\n", StatusBlock.AtaError)); + } + + if ((StatusBlock.AtaError & ATA_ERRREG_MC) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Media Change\n", StatusBlock.AtaError)); + } + + if ((StatusBlock.AtaError & ATA_ERRREG_ABRT) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Abort\n", StatusBlock.AtaError)); + } + + if ((StatusBlock.AtaError & ATA_ERRREG_TK0NF) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Track 0 Not Found\n", StatusBlock.AtaError)); + } + + if ((StatusBlock.AtaError & ATA_ERRREG_AMNF) != 0) { + DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Address Mark Not Found\n", StatusBlock.AtaError)); + } + } + DEBUG_CODE_END (); +} + +/** + This function is used to analyze the Status Register at the condition that BSY is zero. + if there is ERR bit set in the Status Register, then return error. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + + @retval EFI_SUCCESS No err information in the Status Register. + @retval EFI_DEVICE_ERROR Any err information in the Status Register. + +**/ +EFI_STATUS +EFIAPI +CheckStatusRegister ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters + ) +{ + UINT8 StatusRegister; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); + + if ((StatusRegister & ATA_STSREG_BSY) == 0) { + if ((StatusRegister & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) { + return EFI_SUCCESS; + } else { + return EFI_DEVICE_ERROR; + } + } + return EFI_SUCCESS; +} + +/** + This function is used to poll for the DRQ bit clear in the Status + Register. DRQ is cleared when the device is finished transferring data. + So this function is called after data transfer is finished. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS DRQ bit clear within the time out. + + @retval EFI_TIMEOUT DRQ bit not clear within the time out. + + @note + Read Status Register will clear interrupt status. + +**/ +EFI_STATUS +EFIAPI +DRQClear ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT64 Delay; + UINT8 StatusRegister; + BOOLEAN InfiniteWait; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 1000) + 1; + do { + StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); + + // + // Wait for BSY == 0, then judge if DRQ is clear + // + if ((StatusRegister & ATA_STSREG_BSY) == 0) { + if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { + return EFI_DEVICE_ERROR; + } else { + return EFI_SUCCESS; + } + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} +/** + This function is used to poll for the DRQ bit clear in the Alternate + Status Register. DRQ is cleared when the device is finished + transferring data. So this function is called after data transfer + is finished. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS DRQ bit clear within the time out. + + @retval EFI_TIMEOUT DRQ bit not clear within the time out. + @note Read Alternate Status Register will not clear interrupt status. + +**/ +EFI_STATUS +EFIAPI +DRQClear2 ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT64 Delay; + UINT8 AltRegister; + BOOLEAN InfiniteWait; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 1000) + 1; + do { + AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + + // + // Wait for BSY == 0, then judge if DRQ is clear + // + if ((AltRegister & ATA_STSREG_BSY) == 0) { + if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { + return EFI_DEVICE_ERROR; + } else { + return EFI_SUCCESS; + } + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + +/** + This function is used to poll for the DRQ bit set in the + Status Register. + DRQ is set when the device is ready to transfer data. So this function + is called after the command is sent to the device and before required + data is transferred. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS BSY bit cleared and DRQ bit set within the + timeout. + + @retval EFI_TIMEOUT BSY bit not cleared within the timeout. + + @retval EFI_ABORTED Polling abandoned due to command abort. + + @retval EFI_DEVICE_ERROR Polling abandoned due to a non-abort error. + + @retval EFI_NOT_READY BSY bit cleared within timeout, and device + reported "command complete" by clearing DRQ + bit. + + @note Read Status Register will clear interrupt status. + +**/ +EFI_STATUS +EFIAPI +DRQReady ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT64 Delay; + UINT8 StatusRegister; + UINT8 ErrorRegister; + BOOLEAN InfiniteWait; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 1000) + 1; + do { + // + // Read Status Register will clear interrupt + // + StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); + + // + // Wait for BSY == 0, then judge if DRQ is clear or ERR is set + // + if ((StatusRegister & ATA_STSREG_BSY) == 0) { + if ((StatusRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) { + ErrorRegister = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); + + if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) { + return EFI_ABORTED; + } + return EFI_DEVICE_ERROR; + } + + if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { + return EFI_SUCCESS; + } else { + return EFI_NOT_READY; + } + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} +/** + This function is used to poll for the DRQ bit set in the Alternate Status Register. + DRQ is set when the device is ready to transfer data. So this function is called after + the command is sent to the device and before required data is transferred. + + @param PciIo A pointer to EFI_PCI_IO_PROTOCOL data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS BSY bit cleared and DRQ bit set within the + timeout. + + @retval EFI_TIMEOUT BSY bit not cleared within the timeout. + + @retval EFI_ABORTED Polling abandoned due to command abort. + + @retval EFI_DEVICE_ERROR Polling abandoned due to a non-abort error. + + @retval EFI_NOT_READY BSY bit cleared within timeout, and device + reported "command complete" by clearing DRQ + bit. + + @note Read Alternate Status Register will not clear interrupt status. + +**/ +EFI_STATUS +EFIAPI +DRQReady2 ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT64 Delay; + UINT8 AltRegister; + UINT8 ErrorRegister; + BOOLEAN InfiniteWait; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 1000) + 1; + + do { + // + // Read Alternate Status Register will not clear interrupt status + // + AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + // + // Wait for BSY == 0, then judge if DRQ is clear or ERR is set + // + if ((AltRegister & ATA_STSREG_BSY) == 0) { + if ((AltRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) { + ErrorRegister = IdeReadPortB (PciIo, IdeRegisters->ErrOrFeature); + + if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) { + return EFI_ABORTED; + } + return EFI_DEVICE_ERROR; + } + + if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) { + return EFI_SUCCESS; + } else { + return EFI_NOT_READY; + } + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + + + + +/** + This function is used to poll for the BSY bit clear in the Status Register. BSY + is clear when the device is not busy. Every command must be sent after device is not busy. + + @param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS BSY bit clear within the time out. + @retval EFI_TIMEOUT BSY bit not clear within the time out. + + @note Read Status Register will clear interrupt status. +**/ +EFI_STATUS +EFIAPI +WaitForBSYClear ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT64 Delay; + UINT8 StatusRegister; + BOOLEAN InfiniteWait; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32(Timeout, 1000) + 1; + do { + StatusRegister = IdeReadPortB (PciIo, IdeRegisters->CmdOrStatus); + + if ((StatusRegister & ATA_STSREG_BSY) == 0x00) { + return EFI_SUCCESS; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + + +/** + Get IDE i/o port registers' base addresses by mode. + + In 'Compatibility' mode, use fixed addresses. + In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's + Configuration Space. + + The steps to get IDE i/o port registers' base addresses for each channel + as follows: + + 1. Examine the Programming Interface byte of the Class Code fields in PCI IDE + controller's Configuration Space to determine the operating mode. + + 2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below. + ___________________________________________ + | | Command Block | Control Block | + | Channel | Registers | Registers | + |___________|_______________|_______________| + | Primary | 1F0h - 1F7h | 3F6h - 3F7h | + |___________|_______________|_______________| + | Secondary | 170h - 177h | 376h - 377h | + |___________|_______________|_______________| + + Table 1. Compatibility resource mappings + + b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs + in IDE controller's PCI Configuration Space, shown in the Table 2 below. + ___________________________________________________ + | | Command Block | Control Block | + | Channel | Registers | Registers | + |___________|___________________|___________________| + | Primary | BAR at offset 0x10| BAR at offset 0x14| + |___________|___________________|___________________| + | Secondary | BAR at offset 0x18| BAR at offset 0x1C| + |___________|___________________|___________________| + + Table 2. BARs for Register Mapping + + @param[in] PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance + @param[in, out] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to + store the IDE i/o port registers' base addresses + + @retval EFI_UNSUPPORTED Return this value when the BARs is not IO type + @retval EFI_SUCCESS Get the Base address successfully + @retval Other Read the pci configuration data error + +**/ +EFI_STATUS +EFIAPI +GetIdeRegisterIoAddr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN OUT EFI_IDE_REGISTERS *IdeRegisters + ) +{ + EFI_STATUS Status; + PCI_TYPE00 PciData; + UINT16 CommandBlockBaseAddr; + UINT16 ControlBlockBaseAddr; + UINT16 BusMasterBaseAddr; + + if ((PciIo == NULL) || (IdeRegisters == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + BusMasterBaseAddr = (UINT16) ((PciData.Device.Bar[4] & 0x0000fff0)); + + if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) { + CommandBlockBaseAddr = 0x1f0; + ControlBlockBaseAddr = 0x3f6; + } else { + // + // The BARs should be of IO type + // + if ((PciData.Device.Bar[0] & BIT0) == 0 || + (PciData.Device.Bar[1] & BIT0) == 0) { + return EFI_UNSUPPORTED; + } + + CommandBlockBaseAddr = (UINT16) (PciData.Device.Bar[0] & 0x0000fff8); + ControlBlockBaseAddr = (UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2); + } + + // + // Calculate IDE primary channel I/O register base address. + // + IdeRegisters[EfiIdePrimary].Data = CommandBlockBaseAddr; + IdeRegisters[EfiIdePrimary].ErrOrFeature = (UINT16) (CommandBlockBaseAddr + 0x01); + IdeRegisters[EfiIdePrimary].SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02); + IdeRegisters[EfiIdePrimary].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03); + IdeRegisters[EfiIdePrimary].CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04); + IdeRegisters[EfiIdePrimary].CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05); + IdeRegisters[EfiIdePrimary].Head = (UINT16) (CommandBlockBaseAddr + 0x06); + IdeRegisters[EfiIdePrimary].CmdOrStatus = (UINT16) (CommandBlockBaseAddr + 0x07); + IdeRegisters[EfiIdePrimary].AltOrDev = ControlBlockBaseAddr; + IdeRegisters[EfiIdePrimary].BusMasterBaseAddr = BusMasterBaseAddr; + + if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) { + CommandBlockBaseAddr = 0x170; + ControlBlockBaseAddr = 0x376; + } else { + // + // The BARs should be of IO type + // + if ((PciData.Device.Bar[2] & BIT0) == 0 || + (PciData.Device.Bar[3] & BIT0) == 0) { + return EFI_UNSUPPORTED; + } + + CommandBlockBaseAddr = (UINT16) (PciData.Device.Bar[2] & 0x0000fff8); + ControlBlockBaseAddr = (UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2); + } + + // + // Calculate IDE secondary channel I/O register base address. + // + IdeRegisters[EfiIdeSecondary].Data = CommandBlockBaseAddr; + IdeRegisters[EfiIdeSecondary].ErrOrFeature = (UINT16) (CommandBlockBaseAddr + 0x01); + IdeRegisters[EfiIdeSecondary].SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02); + IdeRegisters[EfiIdeSecondary].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03); + IdeRegisters[EfiIdeSecondary].CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04); + IdeRegisters[EfiIdeSecondary].CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05); + IdeRegisters[EfiIdeSecondary].Head = (UINT16) (CommandBlockBaseAddr + 0x06); + IdeRegisters[EfiIdeSecondary].CmdOrStatus = (UINT16) (CommandBlockBaseAddr + 0x07); + IdeRegisters[EfiIdeSecondary].AltOrDev = ControlBlockBaseAddr; + IdeRegisters[EfiIdeSecondary].BusMasterBaseAddr = (UINT16) (BusMasterBaseAddr + 0x8); + + return EFI_SUCCESS; +} + + +/** + Send ATA Ext command into device with NON_DATA protocol. + + @param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. + @param Timeout The time to complete the command, uses 100ns as a unit. + + @retval EFI_SUCCESS Reading succeed + @retval EFI_DEVICE_ERROR Error executing commands on this device. + +**/ +EFI_STATUS +EFIAPI +AtaIssueCommand ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + UINT8 DeviceHead; + UINT8 AtaCommand; + + ASSERT (PciIo != NULL); + ASSERT (IdeRegisters != NULL); + ASSERT (AtaCommandBlock != NULL); + + DeviceHead = AtaCommandBlock->AtaDeviceHead; + AtaCommand = AtaCommandBlock->AtaCommand; + + Status = WaitForBSYClear (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility) + // + IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8) (0xe0 | DeviceHead)); + + // + // set all the command parameters + // Before write to all the following registers, BSY and DRQ must be 0. + // + Status = DRQClear2 (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Fill the feature register, which is a two-byte FIFO. Need write twice. + // + IdeWritePortB (PciIo, IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeaturesExp); + IdeWritePortB (PciIo, IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeatures); + + // + // Fill the sector count register, which is a two-byte FIFO. Need write twice. + // + IdeWritePortB (PciIo, IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCountExp); + IdeWritePortB (PciIo, IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCount); + + // + // Fill the start LBA registers, which are also two-byte FIFO + // + IdeWritePortB (PciIo, IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumberExp); + IdeWritePortB (PciIo, IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumber); + + IdeWritePortB (PciIo, IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLowExp); + IdeWritePortB (PciIo, IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLow); + + IdeWritePortB (PciIo, IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHighExp); + IdeWritePortB (PciIo, IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHigh); + + // + // Send command via Command Register + // + IdeWritePortB (PciIo, IdeRegisters->CmdOrStatus, AtaCommand); + + // + // Stall at least 400 microseconds. + // + MicroSecondDelay (400); + + return EFI_SUCCESS; +} + +/** + This function is used to send out ATA commands conforms to the PIO Data In Protocol. + + @param[in] PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data + structure. + @param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param[in, out] Buffer A pointer to the source buffer for the data. + @param[in] ByteCount The length of the data. + @param[in] Read Flag used to determine the data transfer direction. + Read equals 1, means data transferred from device + to host;Read equals 0, means data transferred + from host to device. + @param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. + @param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + @param[in] Timeout The time to complete the command, uses 100ns as a unit. + @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK + used by non-blocking mode. + + @retval EFI_SUCCESS send out the ATA command and device send required data successfully. + @retval EFI_DEVICE_ERROR command sent failed. + +**/ +EFI_STATUS +EFIAPI +AtaPioDataInOut ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN OUT VOID *Buffer, + IN UINT64 ByteCount, + IN BOOLEAN Read, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, + IN UINT64 Timeout, + IN ATA_NONBLOCK_TASK *Task + ) +{ + UINTN WordCount; + UINTN Increment; + UINT16 *Buffer16; + EFI_STATUS Status; + + if ((PciIo == NULL) || (IdeRegisters == NULL) || (Buffer == NULL) || (AtaCommandBlock == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Issue ATA command + // + Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Buffer16 = (UINT16 *) Buffer; + + // + // According to PIO data in protocol, host can perform a series of reads to + // the data register after each time device set DRQ ready; + // The data size of "a series of read" is command specific. + // For most ATA command, data size received from device will not exceed + // 1 sector, hence the data size for "a series of read" can be the whole data + // size of one command request. + // For ATA command such as Read Sector command, the data size of one ATA + // command request is often larger than 1 sector, according to the + // Read Sector command, the data size of "a series of read" is exactly 1 + // sector. + // Here for simplification reason, we specify the data size for + // "a series of read" to 1 sector (256 words) if data size of one ATA command + // request is larger than 256 words. + // + Increment = 256; + + // + // used to record bytes of currently transferred data + // + WordCount = 0; + + while (WordCount < RShiftU64(ByteCount, 1)) { + // + // Poll DRQ bit set, data transfer can be performed only when DRQ is ready + // + Status = DRQReady2 (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + // + // Get the byte count for one series of read + // + if ((WordCount + Increment) > RShiftU64(ByteCount, 1)) { + Increment = (UINTN)(RShiftU64(ByteCount, 1) - WordCount); + } + + if (Read) { + IdeReadPortWMultiple ( + PciIo, + IdeRegisters->Data, + Increment, + Buffer16 + ); + } else { + IdeWritePortWMultiple ( + PciIo, + IdeRegisters->Data, + Increment, + Buffer16 + ); + } + + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + WordCount += Increment; + Buffer16 += Increment; + } + + Status = DRQClear (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + +Exit: + // + // Dump All Ide registers to ATA_STATUS_BLOCK + // + DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); + + // + // Not support the Non-blocking now,just do the blocking process. + // + return Status; +} + +/** + Send ATA command into device with NON_DATA protocol + + @param[in] PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE + data structure. + @param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data + structure. + @param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + @param[in] Timeout The time to complete the command, uses 100ns as a unit. + @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK + used by non-blocking mode. + + @retval EFI_SUCCESS Reading succeed + @retval EFI_ABORTED Command failed + @retval EFI_DEVICE_ERROR Device status error. + +**/ +EFI_STATUS +EFIAPI +AtaNonDataCommandIn ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, + IN UINT64 Timeout, + IN ATA_NONBLOCK_TASK *Task + ) +{ + EFI_STATUS Status; + + if ((PciIo == NULL) || (IdeRegisters == NULL) || (AtaCommandBlock == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Issue ATA command + // + Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + // + // Wait for command completion + // + Status = WaitForBSYClear (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + +Exit: + // + // Dump All Ide registers to ATA_STATUS_BLOCK + // + DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); + + // + // Not support the Non-blocking now,just do the blocking process. + // + return Status; +} + +/** + Wait for memory to be set. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param[in] Timeout The time to complete the command, uses 100ns as a unit. + + @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 +AtaUdmStatusWait ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT64 Timeout + ) +{ + UINT8 RegisterValue; + EFI_STATUS Status; + UINT16 IoPortForBmis; + UINT64 Delay; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32 (Timeout, 1000) + 1; + + do { + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + break; + } + + IoPortForBmis = (UINT16) (IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); + RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); + if (((RegisterValue & BMIS_ERROR) != 0) || (Timeout == 0)) { + DEBUG ((EFI_D_ERROR, "ATA UDMA operation fails\n")); + Status = EFI_DEVICE_ERROR; + break; + } + + if ((RegisterValue & BMIS_INTERRUPT) != 0) { + Status = EFI_SUCCESS; + break; + } + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + Delay--; + } while (InfiniteWait || (Delay > 0)); + + return Status; +} + +/** + Check if the memory to be set. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK + used by non-blocking mode. + @param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + + @retval EFI_DEVICE_ERROR The memory setting met a issue. + @retval EFI_NOT_READY The memory is not set. + @retval EFI_TIMEOUT The memory setting is time out. + @retval EFI_SUCCESS The memory is correct set. + +**/ +EFI_STATUS +AtaUdmStatusCheck ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN ATA_NONBLOCK_TASK *Task, + IN EFI_IDE_REGISTERS *IdeRegisters + ) +{ + UINT8 RegisterValue; + UINT16 IoPortForBmis; + EFI_STATUS Status; + + Task->RetryTimes--; + + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IoPortForBmis = (UINT16) (IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); + RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); + + if ((RegisterValue & BMIS_ERROR) != 0) { + DEBUG ((EFI_D_ERROR, "ATA UDMA operation fails\n")); + return EFI_DEVICE_ERROR; + } + + if ((RegisterValue & BMIS_INTERRUPT) != 0) { + return EFI_SUCCESS; + } + + if (!Task->InfiniteWait && (Task->RetryTimes == 0)) { + return EFI_TIMEOUT; + } else { + // + // The memory is not set. + // + return EFI_NOT_READY; + } +} + +/** + Perform an ATA Udma operation (Read, ReadExt, Write, WriteExt). + + @param[in] Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data + structure. + @param[in] IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param[in] Read Flag used to determine the data transfer + direction. Read equals 1, means data transferred + from device to host;Read equals 0, means data + transferred from host to device. + @param[in] DataBuffer A pointer to the source buffer for the data. + @param[in] DataLength The length of the data. + @param[in] AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK data structure. + @param[in, out] AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + @param[in] Timeout The time to complete the command, uses 100ns as a unit. + @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK + used by non-blocking mode. + + @retval EFI_SUCCESS the operation is successful. + @retval EFI_OUT_OF_RESOURCES Build PRD table failed + @retval EFI_UNSUPPORTED Unknown channel or operations command + @retval EFI_DEVICE_ERROR Ata command execute failed + +**/ +EFI_STATUS +EFIAPI +AtaUdmaInOut ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN BOOLEAN Read, + IN VOID *DataBuffer, + IN UINT64 DataLength, + IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, + IN UINT64 Timeout, + IN ATA_NONBLOCK_TASK *Task + ) +{ + EFI_STATUS Status; + UINT16 IoPortForBmic; + UINT16 IoPortForBmis; + UINT16 IoPortForBmid; + + UINTN PrdTableSize; + EFI_PHYSICAL_ADDRESS PrdTableMapAddr; + VOID *PrdTableMap; + EFI_PHYSICAL_ADDRESS PrdTableBaseAddr; + EFI_ATA_DMA_PRD *TempPrdBaseAddr; + UINTN PrdTableNum; + + UINT8 RegisterValue; + UINTN PageCount; + UINTN ByteCount; + UINTN ByteRemaining; + UINT8 DeviceControl; + + VOID *BufferMap; + EFI_PHYSICAL_ADDRESS BufferMapAddress; + EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation; + + UINT8 DeviceHead; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_TPL OldTpl; + + UINTN AlignmentMask; + UINTN RealPageCount; + EFI_PHYSICAL_ADDRESS BaseAddr; + EFI_PHYSICAL_ADDRESS BaseMapAddr; + + Status = EFI_SUCCESS; + PrdTableMap = NULL; + BufferMap = NULL; + PageCount = 0; + RealPageCount = 0; + BaseAddr = 0; + PciIo = Instance->PciIo; + + if ((PciIo == NULL) || (IdeRegisters == NULL) || (DataBuffer == NULL) || (AtaCommandBlock == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Before starting the Blocking BlockIO operation, push to finish all non-blocking + // BlockIO tasks. + // Delay 1ms to simulate the blocking time out checking. + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + while ((Task == NULL) && (!IsListEmpty (&Instance->NonBlockingTaskList))) { + AsyncNonBlockingTransferRoutine (NULL, Instance); + // + // Stall for 1 milliseconds. + // + MicroSecondDelay (1000); + } + gBS->RestoreTPL (OldTpl); + + // + // The data buffer should be even alignment + // + if (((UINTN)DataBuffer & 0x1) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Set relevant IO Port address. + // + IoPortForBmic = (UINT16) (IdeRegisters->BusMasterBaseAddr + BMIC_OFFSET); + IoPortForBmis = (UINT16) (IdeRegisters->BusMasterBaseAddr + BMIS_OFFSET); + IoPortForBmid = (UINT16) (IdeRegisters->BusMasterBaseAddr + BMID_OFFSET); + + // + // For Blocking mode, start the command. + // For non-blocking mode, when the command is not started, start it, otherwise + // go to check the status. + // + if (((Task != NULL) && (!Task->IsStart)) || (Task == NULL)) { + // + // Calculate the number of PRD entry. + // Every entry in PRD table can specify a 64K memory region. + // + PrdTableNum = (UINTN)(RShiftU64(DataLength, 16) + 1); + + // + // Make sure that the memory region of PRD table is not cross 64K boundary + // + PrdTableSize = PrdTableNum * sizeof (EFI_ATA_DMA_PRD); + if (PrdTableSize > 0x10000) { + return EFI_INVALID_PARAMETER; + } + + // + // Allocate buffer for PRD table initialization. + // Note Ide Bus Master spec said the descriptor table must be aligned on a 4 byte + // boundary and the table cannot cross a 64K boundary in memory. + // + PageCount = EFI_SIZE_TO_PAGES (PrdTableSize); + RealPageCount = PageCount + EFI_SIZE_TO_PAGES (SIZE_64KB); + + // + // Make sure that PageCount plus EFI_SIZE_TO_PAGES (SIZE_64KB) does not overflow. + // + ASSERT (RealPageCount > PageCount); + + Status = PciIo->AllocateBuffer ( + PciIo, + AllocateAnyPages, + EfiBootServicesData, + RealPageCount, + (VOID **)&BaseAddr, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ByteCount = EFI_PAGES_TO_SIZE (RealPageCount); + Status = PciIo->Map ( + PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + (VOID*)(UINTN)BaseAddr, + &ByteCount, + &BaseMapAddr, + &PrdTableMap + ); + if (EFI_ERROR (Status) || (ByteCount != EFI_PAGES_TO_SIZE (RealPageCount))) { + // + // If the data length actually mapped is not equal to the requested amount, + // it means the DMA operation may be broken into several discontinuous smaller chunks. + // Can't handle this case. + // + PciIo->FreeBuffer (PciIo, RealPageCount, (VOID*)(UINTN)BaseAddr); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem ((VOID *) ((UINTN) BaseAddr), ByteCount); + + // + // Calculate the 64K align address as PRD Table base address. + // + AlignmentMask = SIZE_64KB - 1; + PrdTableBaseAddr = ((UINTN) BaseAddr + AlignmentMask) & ~AlignmentMask; + PrdTableMapAddr = ((UINTN) BaseMapAddr + AlignmentMask) & ~AlignmentMask; + + // + // Map the host address of DataBuffer to DMA master address. + // + if (Read) { + PciIoOperation = EfiPciIoOperationBusMasterWrite; + } else { + PciIoOperation = EfiPciIoOperationBusMasterRead; + } + + ByteCount = (UINTN)DataLength; + Status = PciIo->Map ( + PciIo, + PciIoOperation, + DataBuffer, + &ByteCount, + &BufferMapAddress, + &BufferMap + ); + if (EFI_ERROR (Status) || (ByteCount != DataLength)) { + PciIo->Unmap (PciIo, PrdTableMap); + PciIo->FreeBuffer (PciIo, RealPageCount, (VOID*)(UINTN)BaseAddr); + return EFI_OUT_OF_RESOURCES; + } + + // + // According to Ata spec, it requires the buffer address and size to be even. + // + ASSERT ((BufferMapAddress & 0x1) == 0); + ASSERT ((ByteCount & 0x1) == 0); + + // + // Fill the PRD table with appropriate bus master address of data buffer and data length. + // + ByteRemaining = ByteCount; + TempPrdBaseAddr = (EFI_ATA_DMA_PRD*)(UINTN)PrdTableBaseAddr; + while (ByteRemaining != 0) { + if (ByteRemaining <= 0x10000) { + TempPrdBaseAddr->RegionBaseAddr = (UINT32) ((UINTN) BufferMapAddress); + TempPrdBaseAddr->ByteCount = (UINT16) ByteRemaining; + TempPrdBaseAddr->EndOfTable = 0x8000; + break; + } + + TempPrdBaseAddr->RegionBaseAddr = (UINT32) ((UINTN) BufferMapAddress); + TempPrdBaseAddr->ByteCount = (UINT16) 0x0; + + ByteRemaining -= 0x10000; + BufferMapAddress += 0x10000; + TempPrdBaseAddr++; + } + + // + // Start to enable the DMA operation + // + DeviceHead = AtaCommandBlock->AtaDeviceHead; + + IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)(0xe0 | DeviceHead)); + + // + // Enable interrupt to support UDMA + // + DeviceControl = 0; + IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); + + // + // Read BMIS register and clear ERROR and INTR bit + // + RegisterValue = IdeReadPortB(PciIo, IoPortForBmis); + RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR); + IdeWritePortB (PciIo, IoPortForBmis, RegisterValue); + + // + // Set the base address to BMID register + // + IdeWritePortDW (PciIo, IoPortForBmid, (UINT32)PrdTableMapAddr); + + // + // Set BMIC register to identify the operation direction + // + RegisterValue = IdeReadPortB(PciIo, IoPortForBmic); + if (Read) { + RegisterValue |= BMIC_NREAD; + } else { + RegisterValue &= ~((UINT8) BMIC_NREAD); + } + IdeWritePortB (PciIo, IoPortForBmic, RegisterValue); + + if (Task != NULL) { + Task->Map = BufferMap; + Task->TableMap = PrdTableMap; + Task->MapBaseAddress = (EFI_ATA_DMA_PRD*)(UINTN)BaseAddr; + Task->PageCount = RealPageCount; + Task->IsStart = TRUE; + } + + // + // Issue ATA command + // + Status = AtaIssueCommand (PciIo, IdeRegisters, AtaCommandBlock, Timeout); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // Set START bit of BMIC register + // + RegisterValue = IdeReadPortB(PciIo, IoPortForBmic); + RegisterValue |= BMIC_START; + IdeWritePortB(PciIo, IoPortForBmic, RegisterValue); + + } + + // + // Check the INTERRUPT and ERROR bit of BMIS + // + if (Task != NULL) { + Status = AtaUdmStatusCheck (PciIo, Task, IdeRegisters); + } else { + Status = AtaUdmStatusWait (PciIo, IdeRegisters, Timeout); + } + + // + // For blocking mode, clear registers and free buffers. + // For non blocking mode, when the related registers have been set or time + // out, or a error has been happened, it needs to clear the register and free + // buffer. + // + if ((Task == NULL) || Status != EFI_NOT_READY) { + // + // Read BMIS register and clear ERROR and INTR bit + // + RegisterValue = IdeReadPortB (PciIo, IoPortForBmis); + RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR); + IdeWritePortB (PciIo, IoPortForBmis, RegisterValue); + + // + // Read Status Register of IDE device to clear interrupt + // + RegisterValue = IdeReadPortB(PciIo, IdeRegisters->CmdOrStatus); + + // + // Clear START bit of BMIC register + // + RegisterValue = IdeReadPortB(PciIo, IoPortForBmic); + RegisterValue &= ~((UINT8) BMIC_START); + IdeWritePortB (PciIo, IoPortForBmic, RegisterValue); + + // + // Disable interrupt of Select device + // + DeviceControl = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + DeviceControl |= ATA_CTLREG_IEN_L; + IdeWritePortB (PciIo, IdeRegisters->AltOrDev, DeviceControl); + // + // Stall for 10 milliseconds. + // + MicroSecondDelay (10000); + + } + +Exit: + // + // Free all allocated resource + // + if ((Task == NULL) || Status != EFI_NOT_READY) { + if (Task != NULL) { + PciIo->Unmap (PciIo, Task->TableMap); + PciIo->FreeBuffer (PciIo, Task->PageCount, Task->MapBaseAddress); + PciIo->Unmap (PciIo, Task->Map); + } else { + PciIo->Unmap (PciIo, PrdTableMap); + PciIo->FreeBuffer (PciIo, RealPageCount, (VOID*)(UINTN)BaseAddr); + PciIo->Unmap (PciIo, BufferMap); + } + + // + // Dump All Ide registers to ATA_STATUS_BLOCK + // + DumpAllIdeRegisters (PciIo, IdeRegisters, AtaStatusBlock); + } + + return Status; +} + +/** + This function reads the pending data in the device. + + @param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + + @retval EFI_SUCCESS Successfully read. + @retval EFI_NOT_READY The BSY is set avoiding reading. + +**/ +EFI_STATUS +EFIAPI +AtaPacketReadPendingData ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters + ) +{ + UINT8 AltRegister; + UINT16 TempWordBuffer; + + AltRegister = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + if ((AltRegister & ATA_STSREG_BSY) == ATA_STSREG_BSY) { + return EFI_NOT_READY; + } + + if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) { + TempWordBuffer = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + while ((TempWordBuffer & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) { + IdeReadPortWMultiple ( + PciIo, + IdeRegisters->Data, + 1, + &TempWordBuffer + ); + TempWordBuffer = IdeReadPortB (PciIo, IdeRegisters->AltOrDev); + } + } + return EFI_SUCCESS; +} + +/** + This function is called by AtaPacketCommandExecute(). + It is used to transfer data between host and device. The data direction is specified + by the fourth parameter. + + @param PciIo A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param IdeRegisters A pointer to EFI_IDE_REGISTERS data structure. + @param Buffer Buffer contained data transferred between host and device. + @param ByteCount Data size in byte unit of the buffer. + @param Read Flag used to determine the data transfer direction. + Read equals 1, means data transferred from device to host; + Read equals 0, means data transferred from host to device. + @param Timeout Timeout value for wait DRQ ready before each data stream's transfer + , uses 100ns as a unit. + + @retval EFI_SUCCESS data is transferred successfully. + @retval EFI_DEVICE_ERROR the device failed to transfer data. +**/ +EFI_STATUS +EFIAPI +AtaPacketReadWrite ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN OUT VOID *Buffer, + IN OUT UINT32 *ByteCount, + IN BOOLEAN Read, + IN UINT64 Timeout + ) +{ + UINT32 RequiredWordCount; + UINT32 ActualWordCount; + UINT32 WordCount; + EFI_STATUS Status; + UINT16 *PtrBuffer; + + PtrBuffer = Buffer; + RequiredWordCount = *ByteCount >> 1; + + // + // No data transfer is permitted. + // + if (RequiredWordCount == 0) { + return EFI_SUCCESS; + } + + // + // ActualWordCount means the word count of data really transferred. + // + ActualWordCount = 0; + + while (ActualWordCount < RequiredWordCount) { + // + // before each data transfer stream, the host should poll DRQ bit ready, + // to see whether indicates device is ready to transfer data. + // + Status = DRQReady2 (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_READY) { + // + // Device provided less data than we intended to read, or wanted less + // data than we intended to write, but it may still be successful. + // + break; + } else { + return Status; + } + } + + // + // get current data transfer size from Cylinder Registers. + // + WordCount = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb) << 8; + WordCount = WordCount | IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); + WordCount = WordCount & 0xffff; + WordCount /= 2; + + WordCount = MIN (WordCount, (RequiredWordCount - ActualWordCount)); + + if (Read) { + IdeReadPortWMultiple ( + PciIo, + IdeRegisters->Data, + WordCount, + PtrBuffer + ); + } else { + IdeWritePortWMultiple ( + PciIo, + IdeRegisters->Data, + WordCount, + PtrBuffer + ); + } + + // + // read status register to check whether error happens. + // + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + PtrBuffer += WordCount; + ActualWordCount += WordCount; + } + + if (Read) { + // + // In the case where the drive wants to send more data than we need to read, + // the DRQ bit will be set and cause delays from DRQClear2(). + // We need to read data from the drive until it clears DRQ so we can move on. + // + AtaPacketReadPendingData (PciIo, IdeRegisters); + } + + // + // read status register to check whether error happens. + // + Status = CheckStatusRegister (PciIo, IdeRegisters); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // After data transfer is completed, normally, DRQ bit should clear. + // + Status = DRQClear (PciIo, IdeRegisters, Timeout); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + *ByteCount = ActualWordCount << 1; + return Status; +} + +/** + This function is used to send out ATAPI commands conforms to the Packet Command + with PIO Data In Protocol. + + @param[in] PciIo Pointer to the EFI_PCI_IO_PROTOCOL instance + @param[in] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to + store the IDE i/o port registers' base addresses + @param[in] Channel The channel number of device. + @param[in] Device The device number of device. + @param[in] Packet A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET data structure. + + @retval EFI_SUCCESS send out the ATAPI packet command successfully + and device sends data successfully. + @retval EFI_DEVICE_ERROR the device failed to send data. + +**/ +EFI_STATUS +EFIAPI +AtaPacketCommandExecute ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_IDE_REGISTERS *IdeRegisters, + IN UINT8 Channel, + IN UINT8 Device, + IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + EFI_STATUS Status; + UINT8 Count; + UINT8 PacketCommand[12]; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + // + // Fill ATAPI Command Packet according to CDB. + // For Atapi cmd, its length should be less than or equal to 12 bytes. + // + if (Packet->CdbLength > 12) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (PacketCommand, 12); + CopyMem (PacketCommand, Packet->Cdb, Packet->CdbLength); + + // + // No OVL; No DMA + // + AtaCommandBlock.AtaFeatures = 0x00; + // + // set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device + // determine how many data should be transferred. + // + AtaCommandBlock.AtaCylinderLow = (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff); + AtaCommandBlock.AtaCylinderHigh = (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8); + AtaCommandBlock.AtaDeviceHead = (UINT8) (Device << 0x4); + AtaCommandBlock.AtaCommand = ATA_CMD_PACKET; + + IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)(0xe0 | (Device << 0x4))); + // + // Disable interrupt + // + IdeWritePortB (PciIo, IdeRegisters->AltOrDev, ATA_DEFAULT_CTL); + + // + // Issue ATA PACKET command firstly + // + Status = AtaIssueCommand (PciIo, IdeRegisters, &AtaCommandBlock, Packet->Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DRQReady (PciIo, IdeRegisters, Packet->Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Send out ATAPI command packet + // + for (Count = 0; Count < 6; Count++) { + IdeWritePortW (PciIo, IdeRegisters->Data, *((UINT16*)PacketCommand + Count)); + // + // Stall for 10 microseconds. + // + MicroSecondDelay (10); + } + + // + // Read/Write the data of ATAPI Command + // + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + Status = AtaPacketReadWrite ( + PciIo, + IdeRegisters, + Packet->InDataBuffer, + &Packet->InTransferLength, + TRUE, + Packet->Timeout + ); + } else { + Status = AtaPacketReadWrite ( + PciIo, + IdeRegisters, + Packet->OutDataBuffer, + &Packet->OutTransferLength, + FALSE, + Packet->Timeout + ); + } + + return Status; +} + + +/** + Set the calculated Best transfer mode to a detected device. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param TransferMode A pointer to EFI_ATA_TRANSFER_MODE data structure. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Set transfer mode successfully. + @retval EFI_DEVICE_ERROR Set transfer mode failed. + @retval EFI_OUT_OF_RESOURCES Allocate memory failed. + +**/ +EFI_STATUS +EFIAPI +SetDeviceTransferMode ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN EFI_ATA_TRANSFER_MODE *TransferMode, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SET_FEATURES; + AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); + AtaCommandBlock.AtaFeatures = 0x03; + AtaCommandBlock.AtaSectorCount = *((UINT8 *)TransferMode); + + // + // Send SET FEATURE command (sub command 0x03) to set pio mode. + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + return Status; +} + +/** + Set drive parameters for devices not support PACKETS command. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param DriveParameters A pointer to EFI_ATA_DRIVE_PARMS data structure. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Set drive parameter successfully. + @retval EFI_DEVICE_ERROR Set drive parameter failed. + @retval EFI_OUT_OF_RESOURCES Allocate memory failed. + +**/ +EFI_STATUS +EFIAPI +SetDriveParameters ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN EFI_ATA_DRIVE_PARMS *DriveParameters, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_INIT_DRIVE_PARAM; + AtaCommandBlock.AtaSectorCount = DriveParameters->Sector; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) + DriveParameters->Heads); + + // + // Send Init drive parameters + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + // + // Send Set Multiple parameters + // + AtaCommandBlock.AtaCommand = ATA_CMD_SET_MULTIPLE_MODE; + AtaCommandBlock.AtaSectorCount = DriveParameters->MultipleSector; + AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); + + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + return Status; +} + +/** + Send SMART Return Status command to check if the execution of SMART cmd is successful or not. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution. + @retval Others Fail to get return status data. + +**/ +EFI_STATUS +EFIAPI +IdeAtaSmartReturnStatusCheck ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + UINT8 LBAMid; + UINT8 LBAHigh; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + // + // Send S.M.A.R.T Read Return Status command to device + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED) + ); + return EFI_DEVICE_ERROR; + } + + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE) + ); + + LBAMid = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderLsb); + LBAHigh = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderMsb); + + if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) { + // + // The threshold exceeded condition is not detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n")); + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD) + ); + } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) { + // + // The threshold exceeded condition is detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n")); + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD) + ); + } + + return EFI_SUCCESS; +} + +/** + Enable SMART command of the disk if supported. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + +**/ +VOID +EFIAPI +IdeAtaSmartSupport ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN EFI_IDENTIFY_DATA *IdentifyData, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + // + // Detect if the device supports S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) { + // + // S.M.A.R.T is not supported by the device + // + DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at [%a] channel [%a] device!\n", + (Channel == 1) ? "secondary" : "primary", (Device == 1) ? "slave" : "master")); + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED) + ); + } else { + // + // Check if the feature is enabled. If not, then enable S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) { + + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE) + ); + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + // + // Send S.M.A.R.T Enable command to device + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + if (!EFI_ERROR (Status)) { + // + // Send S.M.A.R.T AutoSave command to device + // + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = 0xD2; + AtaCommandBlock.AtaSectorCount = 0xF1; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + if (!EFI_ERROR (Status)) { + Status = IdeAtaSmartReturnStatusCheck ( + Instance, + Channel, + Device, + AtaStatusBlock + ); + } + } + } + + DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at [%a] channel [%a] device!\n", + (Channel == 1) ? "secondary" : "primary", (Device == 1) ? "slave" : "master")); + + } + + return ; +} + + +/** + Sends out an ATA Identify Command to the specified device. + + This function is called by DiscoverIdeDevice() during its device + identification. It sends out the ATA Identify Command to the + specified device. Only ATA device responses to this command. If + the command succeeds, it returns the Identify data structure which + contains information about the device. This function extracts the + information it needs to fill the IDE_BLK_IO_DEV data structure, + including device type, media block size, media capacity, and etc. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param Buffer A pointer to data buffer which is used to contain IDENTIFY data. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Identify ATA device successfully. + @retval EFI_DEVICE_ERROR ATA Identify Device Command failed or device is not ATA device. + @retval EFI_OUT_OF_RESOURCES Allocate memory failed. + +**/ +EFI_STATUS +EFIAPI +AtaIdentify ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN OUT EFI_IDENTIFY_DATA *Buffer, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE; + AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); + + Status = AtaPioDataInOut ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + Buffer, + sizeof (EFI_IDENTIFY_DATA), + TRUE, + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + return Status; +} + +/** + This function is called by DiscoverIdeDevice() during its device + identification. + Its main purpose is to get enough information for the device media + to fill in the Media data structure of the Block I/O Protocol interface. + + There are 5 steps to reach such objective: + 1. Sends out the ATAPI Identify Command to the specified device. + Only ATAPI device responses to this command. If the command succeeds, + it returns the Identify data structure which filled with information + about the device. Since the ATAPI device contains removable media, + the only meaningful information is the device module name. + 2. Sends out ATAPI Inquiry Packet Command to the specified device. + This command will return inquiry data of the device, which contains + the device type information. + 3. Allocate sense data space for future use. We don't detect the media + presence here to improvement boot performance, especially when CD + media is present. The media detection will be performed just before + each BLK_IO read/write + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param Buffer A pointer to data buffer which is used to contain IDENTIFY data. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Identify ATAPI device successfully. + @retval EFI_DEVICE_ERROR ATA Identify Packet Device Command failed or device type + is not supported by this IDE driver. + @retval EFI_OUT_OF_RESOURCES Allocate memory failed. + +**/ +EFI_STATUS +EFIAPI +AtaIdentifyPacket ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN OUT EFI_IDENTIFY_DATA *Buffer, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DEVICE; + AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4); + + // + // Send ATAPI Identify Command to get IDENTIFY data. + // + Status = AtaPioDataInOut ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + (VOID *) Buffer, + sizeof (EFI_IDENTIFY_DATA), + TRUE, + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT, + NULL + ); + + return Status; +} + + +/** + This function is used for detect whether the IDE device exists in the + specified Channel as the specified Device Number. + + There is two IDE channels: one is Primary Channel, the other is + Secondary Channel.(Channel is the logical name for the physical "Cable".) + Different channel has different register group. + + On each IDE channel, at most two IDE devices attach, + one is called Device 0 (Master device), the other is called Device 1 + (Slave device). The devices on the same channel co-use the same register + group, so before sending out a command for a specified device via command + register, it is a must to select the current device to accept the command + by set the device number in the Head/Device Register. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param IdeChannel The channel number of device. + + @retval EFI_SUCCESS successfully detects device. + @retval other any failure during detection process will return this value. + +**/ +EFI_STATUS +EFIAPI +DetectAndConfigIdeDevice ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 IdeChannel + ) +{ + EFI_STATUS Status; + UINT8 SectorCountReg; + UINT8 LBALowReg; + UINT8 LBAMidReg; + UINT8 LBAHighReg; + EFI_ATA_DEVICE_TYPE DeviceType; + UINT8 IdeDevice; + EFI_IDE_REGISTERS *IdeRegisters; + EFI_IDENTIFY_DATA Buffer; + + EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; + EFI_PCI_IO_PROTOCOL *PciIo; + + EFI_ATA_COLLECTIVE_MODE *SupportedModes; + EFI_ATA_TRANSFER_MODE TransferMode; + EFI_ATA_DRIVE_PARMS DriveParameters; + + IdeRegisters = &Instance->IdeRegisters[IdeChannel]; + IdeInit = Instance->IdeControllerInit; + PciIo = Instance->PciIo; + + for (IdeDevice = 0; IdeDevice < EfiIdeMaxDevice; IdeDevice++) { + // + // Select Master or Slave device to get the return signature for ATA DEVICE DIAGNOSTIC cmd. + // + IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)((IdeDevice << 4) | 0xe0)); + + // + // Send ATA Device Execut Diagnostic command. + // This command should work no matter DRDY is ready or not + // + IdeWritePortB (PciIo, IdeRegisters->CmdOrStatus, ATA_CMD_EXEC_DRIVE_DIAG); + + Status = WaitForBSYClear (PciIo, IdeRegisters, 350000000); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status)); + continue; + } + + // + // Select Master or Slave device to get the return signature for ATA DEVICE DIAGNOSTIC cmd. + // + IdeWritePortB (PciIo, IdeRegisters->Head, (UINT8)((IdeDevice << 4) | 0xe0)); + // + // Stall for 1 milliseconds. + // + MicroSecondDelay (1000); + + SectorCountReg = IdeReadPortB (PciIo, IdeRegisters->SectorCount); + LBALowReg = IdeReadPortB (PciIo, IdeRegisters->SectorNumber); + LBAMidReg = IdeReadPortB (PciIo, IdeRegisters->CylinderLsb); + LBAHighReg = IdeReadPortB (PciIo, IdeRegisters->CylinderMsb); + + // + // Refer to ATA/ATAPI 4 Spec, section 9.1 + // + if ((SectorCountReg == 0x1) && (LBALowReg == 0x1) && (LBAMidReg == 0x0) && (LBAHighReg == 0x0)) { + DeviceType = EfiIdeHarddisk; + } else if ((LBAMidReg == 0x14) && (LBAHighReg == 0xeb)) { + DeviceType = EfiIdeCdrom; + } else { + continue; + } + + // + // Send IDENTIFY cmd to the device to test if it is really attached. + // + if (DeviceType == EfiIdeHarddisk) { + Status = AtaIdentify (Instance, IdeChannel, IdeDevice, &Buffer, NULL); + // + // if identifying ata device is failure, then try to send identify packet cmd. + // + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED)); + + DeviceType = EfiIdeCdrom; + Status = AtaIdentifyPacket (Instance, IdeChannel, IdeDevice, &Buffer, NULL); + } + } else { + Status = AtaIdentifyPacket (Instance, IdeChannel, IdeDevice, &Buffer, NULL); + // + // if identifying atapi device is failure, then try to send identify cmd. + // + if (EFI_ERROR (Status)) { + DeviceType = EfiIdeHarddisk; + Status = AtaIdentify (Instance, IdeChannel, IdeDevice, &Buffer, NULL); + } + } + + if (EFI_ERROR (Status)) { + // + // No device is found at this port + // + continue; + } + + DEBUG ((EFI_D_INFO, "[%a] channel [%a] [%a] device\n", + (IdeChannel == 1) ? "secondary" : "primary ", (IdeDevice == 1) ? "slave " : "master", + DeviceType == EfiIdeCdrom ? "cdrom " : "harddisk")); + // + // If the device is a hard disk, then try to enable S.M.A.R.T feature + // + if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) { + IdeAtaSmartSupport ( + Instance, + IdeChannel, + IdeDevice, + &Buffer, + NULL + ); + } + + // + // Submit identify data to IDE controller init driver + // + IdeInit->SubmitData (IdeInit, IdeChannel, IdeDevice, &Buffer); + + // + // Now start to config ide device parameter and transfer mode. + // + Status = IdeInit->CalculateMode ( + IdeInit, + IdeChannel, + IdeDevice, + &SupportedModes + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Calculate Mode Fail, Status = %r\n", Status)); + continue; + } + + // + // Set best supported PIO mode on this IDE device + // + if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) { + TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO; + } else { + TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO; + } + + TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode); + + if (SupportedModes->ExtModeCount == 0){ + Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); + continue; + } + } + + // + // Set supported DMA mode on this IDE device. Note that UDMA & MDMA can't + // be set together. Only one DMA mode can be set to a device. If setting + // DMA mode operation fails, we can continue moving on because we only use + // PIO mode at boot time. DMA modes are used by certain kind of OS booting + // + if (SupportedModes->UdmaMode.Valid) { + TransferMode.ModeCategory = EFI_ATA_MODE_UDMA; + TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode); + Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); + continue; + } + } else if (SupportedModes->MultiWordDmaMode.Valid) { + TransferMode.ModeCategory = EFI_ATA_MODE_MDMA; + TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode; + Status = SetDeviceTransferMode (Instance, IdeChannel, IdeDevice, &TransferMode, NULL); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); + continue; + } + } + + // + // Set Parameters for the device: + // 1) Init + // 2) Establish the block count for READ/WRITE MULTIPLE (EXT) command + // + if (DeviceType == EfiIdeHarddisk) { + // + // Init driver parameters + // + DriveParameters.Sector = (UINT8) ((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->sectors_per_track; + DriveParameters.Heads = (UINT8) (((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->heads - 1); + DriveParameters.MultipleSector = (UINT8) ((ATA5_IDENTIFY_DATA *)(&Buffer.AtaData))->multi_sector_cmd_max_sct_cnt; + + Status = SetDriveParameters (Instance, IdeChannel, IdeDevice, &DriveParameters, NULL); + } + + // + // Set IDE controller Timing Blocks in the PCI Configuration Space + // + IdeInit->SetTiming (IdeInit, IdeChannel, IdeDevice, SupportedModes); + + // + // IDE controller and IDE device timing is configured successfully. + // Now insert the device into device list. + // + Status = CreateNewDeviceInfo (Instance, IdeChannel, IdeDevice, DeviceType, &Buffer); + if (EFI_ERROR (Status)) { + continue; + } + + if (DeviceType == EfiIdeHarddisk) { + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE)); + } + } + return EFI_SUCCESS; +} + + +/** + Initialize ATA host controller at IDE mode. + + The function is designed to initialize ATA host controller. + + @param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. + +**/ +EFI_STATUS +EFIAPI +IdeModeInitialization ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Channel; + UINT8 IdeChannel; + BOOLEAN ChannelEnabled; + UINT8 MaxDevices; + + IdeInit = Instance->IdeControllerInit; + PciIo = Instance->PciIo; + Channel = IdeInit->ChannelCount; + + // + // Obtain IDE IO port registers' base addresses + // + Status = GetIdeRegisterIoAddr (PciIo, Instance->IdeRegisters); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + for (IdeChannel = 0; IdeChannel < Channel; IdeChannel++) { + IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, IdeChannel); + + // + // now obtain channel information fron IdeControllerInit protocol. + // + Status = IdeInit->GetChannelInfo ( + IdeInit, + IdeChannel, + &ChannelEnabled, + &MaxDevices + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "[GetChannel, Status=%x]", Status)); + continue; + } + + if (!ChannelEnabled) { + continue; + } + + ASSERT (MaxDevices <= 2); + // + // Now inform the IDE Controller Init Module. + // + IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelReset, IdeChannel); + + // + // No reset channel function implemented. + // + IdeInit->NotifyPhase (IdeInit, EfiIdeAfterChannelReset, IdeChannel); + + // + // Now inform the IDE Controller Init Module. + // + IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, IdeChannel); + + // + // Detect all attached ATA devices and set the transfer mode for each device. + // + DetectAndConfigIdeDevice (Instance, IdeChannel); + } + + // + // All configurations done! Notify IdeController to do post initialization + // work such as saving IDE controller PCI settings for S3 resume + // + IdeInit->NotifyPhase (IdeInit, EfiIdeBusPhaseMaximum, 0); + +ErrorExit: + return Status; +} + -- cgit 1.2.3-korg