git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@8004 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2731 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2731 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Copyright (c) 2006 - 2008, Intel Corporation.<BR>
 | 
						|
  All rights reserved. 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.
 | 
						|
 | 
						|
  @par Revision Reference:
 | 
						|
  2002-6: Add Atapi6 enhancement, support >120GB hard disk, including
 | 
						|
  update - ATAIdentity() func
 | 
						|
  update - AtaBlockIoReadBlocks() func
 | 
						|
  update - AtaBlockIoWriteBlocks() func
 | 
						|
  add    - AtaAtapi6Identify() func
 | 
						|
  add    - AtaReadSectorsExt() func
 | 
						|
  add    - AtaWriteSectorsExt() func
 | 
						|
  add    - AtaPioDataInExt() func
 | 
						|
  add    - AtaPioDataOutExt() func
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "IdeBus.h"
 | 
						|
 | 
						|
/**
 | 
						|
  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[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure,used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS Identify ATA device successfully.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR ATA Identify Device Command failed or
 | 
						|
  device is not ATA device.
 | 
						|
 | 
						|
  @note
 | 
						|
  parameter IdeDev will be updated in this function.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ATAIdentify (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS        Status;
 | 
						|
  EFI_IDENTIFY_DATA *AtaIdentifyPointer;
 | 
						|
  UINT32            Capacity;
 | 
						|
  UINT8             DeviceSelect;
 | 
						|
  UINTN				Retry;
 | 
						|
 | 
						|
  //
 | 
						|
  //  AtaIdentifyPointer is used for accommodating returned IDENTIFY data of
 | 
						|
  //  the ATA Identify command
 | 
						|
  //
 | 
						|
  AtaIdentifyPointer = (EFI_IDENTIFY_DATA *) AllocateZeroPool (sizeof (EFI_IDENTIFY_DATA));
 | 
						|
  if (AtaIdentifyPointer == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  use ATA PIO Data In protocol to send ATA Identify command
 | 
						|
  //  and receive data from device
 | 
						|
  //
 | 
						|
  DeviceSelect  = (UINT8) ((IdeDev->Device) << 4);
 | 
						|
 | 
						|
  
 | 
						|
  Retry = 3;
 | 
						|
  while (Retry > 0) {	
 | 
						|
    Status = AtaPioDataIn (
 | 
						|
              IdeDev,
 | 
						|
              (VOID *) AtaIdentifyPointer,
 | 
						|
              sizeof (EFI_IDENTIFY_DATA),
 | 
						|
              ATA_CMD_IDENTIFY_DRIVE,
 | 
						|
              DeviceSelect,
 | 
						|
              0,
 | 
						|
              0,
 | 
						|
              0,
 | 
						|
              0
 | 
						|
              );
 | 
						|
    //
 | 
						|
    // If ATA Identify command succeeds, then according to the received
 | 
						|
    // IDENTIFY data,
 | 
						|
    // identify the device type ( ATA or not ).
 | 
						|
    // If ATA device, fill the information in IdeDev.
 | 
						|
    // If not ATA device, return IDE_DEVICE_ERROR
 | 
						|
    //
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
 | 
						|
      IdeDev->pIdData = AtaIdentifyPointer;
 | 
						|
 | 
						|
      //
 | 
						|
      // Print ATA Module Name
 | 
						|
      //
 | 
						|
      PrintAtaModuleName (IdeDev);
 | 
						|
 | 
						|
      //
 | 
						|
      // bit 15 of pAtaIdentify->config is used to identify whether device is
 | 
						|
      // ATA device or ATAPI device.
 | 
						|
      // if 0, means ATA device; if 1, means ATAPI device.
 | 
						|
      //
 | 
						|
      if ((AtaIdentifyPointer->AtaData.config & 0x8000) == 0x00) {
 | 
						|
        //
 | 
						|
        // Detect if support S.M.A.R.T. If yes, enable it as default
 | 
						|
        //
 | 
						|
        AtaSMARTSupport (IdeDev);
 | 
						|
 | 
						|
        //
 | 
						|
        // Check whether this device needs 48-bit addressing (ATAPI-6 ata device)
 | 
						|
        //
 | 
						|
        Status = AtaAtapi6Identify (IdeDev);
 | 
						|
        if (!EFI_ERROR (Status)) {
 | 
						|
          //
 | 
						|
          // It's a disk with >120GB capacity, initialized in AtaAtapi6Identify()
 | 
						|
          //
 | 
						|
          return EFI_SUCCESS;
 | 
						|
        } else if (Status == EFI_DEVICE_ERROR) {
 | 
						|
		  //
 | 
						|
		  // Some disk with big capacity (>200GB) is slow when being identified
 | 
						|
		  // and will return all zero for word83.
 | 
						|
		  // We try twice at first. If it fails, we do a SoftRest and try again.
 | 
						|
		  //
 | 
						|
		  Retry--;
 | 
						|
		  if (Retry == 1) {
 | 
						|
		    //
 | 
						|
		    // Do a SoftRest before the third attempt.
 | 
						|
		    //
 | 
						|
		    AtaSoftReset (IdeDev);
 | 
						|
		  }
 | 
						|
		  continue;
 | 
						|
	    }
 | 
						|
        //
 | 
						|
        // This is a hard disk <= 120GB capacity, treat it as normal hard disk
 | 
						|
        //
 | 
						|
        IdeDev->Type = IdeHardDisk;
 | 
						|
 | 
						|
        //
 | 
						|
        // Block Media Information:
 | 
						|
        // Media->LogicalPartition , Media->WriteCaching will be filled
 | 
						|
        // in the DiscoverIdeDevcie() function.
 | 
						|
        //
 | 
						|
        IdeDev->BlkIo.Media->IoAlign        = 4;
 | 
						|
        IdeDev->BlkIo.Media->MediaId        = 1;
 | 
						|
        IdeDev->BlkIo.Media->RemovableMedia = FALSE;
 | 
						|
        IdeDev->BlkIo.Media->MediaPresent   = TRUE;
 | 
						|
        IdeDev->BlkIo.Media->ReadOnly       = FALSE;
 | 
						|
        IdeDev->BlkIo.Media->BlockSize      = 0x200;
 | 
						|
 | 
						|
        //
 | 
						|
        // Calculate device capacity
 | 
						|
        //
 | 
						|
        Capacity = ((UINT32)AtaIdentifyPointer->AtaData.user_addressable_sectors_hi << 16) |
 | 
						|
                  AtaIdentifyPointer->AtaData.user_addressable_sectors_lo ;
 | 
						|
        IdeDev->BlkIo.Media->LastBlock = Capacity - 1;
 | 
						|
 | 
						|
        return EFI_SUCCESS;
 | 
						|
      }
 | 
						|
 | 
						|
    }
 | 
						|
  	break;
 | 
						|
  }
 | 
						|
 | 
						|
  gBS->FreePool (AtaIdentifyPointer);
 | 
						|
  //
 | 
						|
  // Make sure the pIdData will not be freed again.
 | 
						|
  //
 | 
						|
  IdeDev->pIdData = NULL;
 | 
						|
 | 
						|
  return EFI_DEVICE_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by ATAIdentify() to identity whether this disk
 | 
						|
  supports ATA/ATAPI6 48bit addressing, ie support >120G capacity
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @retval  EFI_SUCCESS The disk specified by IdeDev is a Atapi6 supported one
 | 
						|
  and 48-bit addressing must be used
 | 
						|
 | 
						|
  @retval  EFI_UNSUPPORTED The disk dosn't not support Atapi6 or it supports but
 | 
						|
  the capacity is below 120G, 48bit addressing is not needed
 | 
						|
 | 
						|
  @retval  EFI_DEVICE_ERROR The identify data in IdeDev is incorrect
 | 
						|
  
 | 
						|
  @retval  EFI_INVALID_PARAMETER The identify data in IdeDev is NULL.
 | 
						|
 | 
						|
  @note
 | 
						|
  This function must be called after DEVICE_IDENTITY command has been
 | 
						|
  successfully returned
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaAtapi6Identify (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8             Index;
 | 
						|
  EFI_LBA           TmpLba;
 | 
						|
  EFI_LBA           Capacity;
 | 
						|
  EFI_IDENTIFY_DATA *Atapi6IdentifyStruct;
 | 
						|
 | 
						|
  if (IdeDev->pIdData == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Atapi6IdentifyStruct = IdeDev->pIdData;
 | 
						|
 | 
						|
  if ((Atapi6IdentifyStruct->AtapiData.cmd_set_support_83 & (BIT15 | BIT14)) != 0x4000) {
 | 
						|
    //
 | 
						|
    // Per ATA-6 spec, word83: bit15 is zero and bit14 is one
 | 
						|
    //
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Atapi6IdentifyStruct->AtapiData.cmd_set_support_83 & BIT10) == 0) {
 | 
						|
    //
 | 
						|
    // The device dosn't support 48 bit addressing
 | 
						|
    //
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // 48 bit address feature set is supported, get maximum capacity
 | 
						|
  //
 | 
						|
  Capacity = Atapi6IdentifyStruct->AtapiData.max_user_lba_for_48bit_addr[0];
 | 
						|
  for (Index = 1; Index < 4; Index++) {
 | 
						|
    //
 | 
						|
    // Lower byte goes first: word[100] is the lowest word, word[103] is highest
 | 
						|
    //
 | 
						|
    TmpLba = Atapi6IdentifyStruct->AtapiData.max_user_lba_for_48bit_addr[Index];
 | 
						|
    Capacity |= LShiftU64 (TmpLba, 16 * Index);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {
 | 
						|
    //
 | 
						|
    // Capacity exceeds 120GB. 48-bit addressing is really needed
 | 
						|
    //
 | 
						|
    IdeDev->Type = Ide48bitAddressingHardDisk;
 | 
						|
 | 
						|
    //
 | 
						|
    // Fill block media information:Media->LogicalPartition ,
 | 
						|
    // Media->WriteCaching will be filledin the DiscoverIdeDevcie() function.
 | 
						|
    //
 | 
						|
    IdeDev->BlkIo.Media->IoAlign        = 4;
 | 
						|
    IdeDev->BlkIo.Media->MediaId        = 1;
 | 
						|
    IdeDev->BlkIo.Media->RemovableMedia = FALSE;
 | 
						|
    IdeDev->BlkIo.Media->MediaPresent   = TRUE;
 | 
						|
    IdeDev->BlkIo.Media->ReadOnly       = FALSE;
 | 
						|
    IdeDev->BlkIo.Media->BlockSize      = 0x200;
 | 
						|
    IdeDev->BlkIo.Media->LastBlock      = Capacity - 1;
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by ATAIdentify() or ATAPIIdentify()
 | 
						|
  to print device's module name.
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
PrintAtaModuleName (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (IdeDev->pIdData == NULL) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  SwapStringChars (IdeDev->ModelName, IdeDev->pIdData->AtaData.ModelName, 40);
 | 
						|
  IdeDev->ModelName[40] = 0x00;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is used to send out ATA commands conforms to the
 | 
						|
  PIO Data In Protocol.
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *Buffer
 | 
						|
  buffer contained data transferred from device to host.
 | 
						|
 | 
						|
  @param[in] ByteCount
 | 
						|
  data size in byte unit of the buffer.
 | 
						|
 | 
						|
  @param[in] AtaCommand
 | 
						|
  value of the Command Register
 | 
						|
 | 
						|
  @param[in] Head
 | 
						|
  value of the Head/Device Register
 | 
						|
 | 
						|
  @param[in] SectorCount
 | 
						|
  value of the Sector Count Register
 | 
						|
 | 
						|
  @param[in] SectorNumber
 | 
						|
  value of the Sector Number Register
 | 
						|
 | 
						|
  @param[in] CylinderLsb
 | 
						|
  value of the low byte of the Cylinder Register
 | 
						|
 | 
						|
  @param[in] CylinderMsb
 | 
						|
  value of the high byte of the Cylinder Register
 | 
						|
 | 
						|
  @retval EFI_SUCCESS send out the ATA command and device send required
 | 
						|
  data successfully.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR command sent failed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaPioDataIn (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *Buffer,
 | 
						|
  IN  UINT32          ByteCount,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  UINT8           Head,
 | 
						|
  IN  UINT8           SectorCount,
 | 
						|
  IN  UINT8           SectorNumber,
 | 
						|
  IN  UINT8           CylinderLsb,
 | 
						|
  IN  UINT8           CylinderMsb
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       WordCount;
 | 
						|
  UINTN       Increment;
 | 
						|
  UINT16      *Buffer16;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  e0:1110,0000-- bit7 and bit5 are reserved bits.
 | 
						|
  //           bit6 set means LBA mode
 | 
						|
  //
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    (UINT8) ((IdeDev->Device << 4) | 0xe0 | Head)
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // All ATAPI device's ATA commands can be issued regardless of the
 | 
						|
  // state of the DRDY
 | 
						|
  //
 | 
						|
  if (IdeDev->Type == IdeHardDisk) {
 | 
						|
 | 
						|
    Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // set all the command parameters
 | 
						|
  // Before write to all the following registers, BSY and DRQ must be 0.
 | 
						|
  //
 | 
						|
  Status = DRQClear2 (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  if (AtaCommand == ATA_CMD_SET_FEATURES) {
 | 
						|
    IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
 | 
						|
  }
 | 
						|
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, SectorNumber);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, CylinderLsb);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, CylinderMsb);
 | 
						|
 | 
						|
  //
 | 
						|
  // send command via Command Register
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  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 transfered data
 | 
						|
  //
 | 
						|
  WordCount = 0;
 | 
						|
 | 
						|
  while (WordCount < ByteCount / 2) {
 | 
						|
    //
 | 
						|
    // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
 | 
						|
    //
 | 
						|
    Status = DRQReady2 (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = CheckErrorStatus (IdeDev);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Get the byte count for one series of read
 | 
						|
    //
 | 
						|
    if ((WordCount + Increment) > ByteCount / 2) {
 | 
						|
      Increment = ByteCount / 2 - WordCount;
 | 
						|
    }
 | 
						|
 | 
						|
    IDEReadPortWMultiple (
 | 
						|
      IdeDev->PciIo,
 | 
						|
      IdeDev->IoPort->Data,
 | 
						|
      Increment,
 | 
						|
      Buffer16
 | 
						|
      );
 | 
						|
 | 
						|
    WordCount += Increment;
 | 
						|
    Buffer16 += Increment;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  DRQClear (IdeDev, ATATIMEOUT);
 | 
						|
 | 
						|
  return CheckErrorStatus (IdeDev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is used to send out ATA commands conforms to the
 | 
						|
  PIO Data Out Protocol.
 | 
						|
 | 
						|
  @param *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param *Buffer      buffer contained data transferred from host to device.
 | 
						|
  @param ByteCount    data size in byte unit of the buffer.
 | 
						|
  @param AtaCommand   value of the Command Register
 | 
						|
  @param Head         value of the Head/Device Register
 | 
						|
  @param SectorCount  value of the Sector Count Register
 | 
						|
  @param SectorNumber value of the Sector Number Register
 | 
						|
  @param CylinderLsb  value of the low byte of the Cylinder Register
 | 
						|
  @param CylinderMsb  value of the high byte of the Cylinder Register
 | 
						|
 | 
						|
  @retval EFI_SUCCESS send out the ATA command and device received required
 | 
						|
  data successfully.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR command sent failed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaPioDataOut (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *Buffer,
 | 
						|
  IN  UINT32          ByteCount,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  UINT8           Head,
 | 
						|
  IN  UINT8           SectorCount,
 | 
						|
  IN  UINT8           SectorNumber,
 | 
						|
  IN  UINT8           CylinderLsb,
 | 
						|
  IN  UINT8           CylinderMsb
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       WordCount;
 | 
						|
  UINTN       Increment;
 | 
						|
  UINT16      *Buffer16;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // select device via Head/Device register.
 | 
						|
  // Before write Head/Device register, BSY and DRQ must be 0.
 | 
						|
  //
 | 
						|
  Status = DRQClear2 (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // e0:1110,0000-- bit7 and bit5 are reserved bits.
 | 
						|
  //          bit6 set means LBA mode
 | 
						|
  //
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    (UINT8) ((IdeDev->Device << 4) | 0xe0 | Head)
 | 
						|
    );
 | 
						|
 | 
						|
  Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // set all the command parameters
 | 
						|
  // Before write to all the following registers, BSY and DRQ must be 0.
 | 
						|
  //
 | 
						|
  Status = DRQClear2 (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, SectorNumber);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, CylinderLsb);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, CylinderMsb);
 | 
						|
 | 
						|
  //
 | 
						|
  // send command via Command Register
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  Buffer16 = (UINT16 *) Buffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // According to PIO data out protocol, host can perform a series of
 | 
						|
  // writes 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 written to device will not exceed 1 sector,
 | 
						|
  // hence the data size for "a series of write" can be the data size of one
 | 
						|
  // command request.
 | 
						|
  // For ATA command such as Write Sector command, the data size of one
 | 
						|
  // ATA command request is often larger than 1 sector, according to the
 | 
						|
  // Write 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 write" to 1 sector (256 words) if data size of one ATA command
 | 
						|
  // request is larger than 256 words.
 | 
						|
  //
 | 
						|
  Increment = 256;
 | 
						|
  WordCount = 0;
 | 
						|
 | 
						|
  while (WordCount < ByteCount / 2) {
 | 
						|
 | 
						|
    //
 | 
						|
    // DRQReady2-- read Alternate Status Register to determine the DRQ bit
 | 
						|
    // data transfer can be performed only when DRQ is ready.
 | 
						|
    //
 | 
						|
    Status = DRQReady2 (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = CheckErrorStatus (IdeDev);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
   //
 | 
						|
   // Check the remaining byte count is less than 512 bytes
 | 
						|
   //
 | 
						|
   if ((WordCount + Increment) > ByteCount / 2) {
 | 
						|
      Increment = ByteCount / 2 - WordCount;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // perform a series of write without check DRQ ready
 | 
						|
    //
 | 
						|
 | 
						|
    IDEWritePortWMultiple (
 | 
						|
      IdeDev->PciIo,
 | 
						|
      IdeDev->IoPort->Data,
 | 
						|
      Increment,
 | 
						|
      Buffer16
 | 
						|
      );
 | 
						|
    WordCount += Increment;
 | 
						|
    Buffer16 += Increment;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  DRQClear (IdeDev, ATATIMEOUT);
 | 
						|
 | 
						|
  return CheckErrorStatus (IdeDev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       No err information in the Status Register.
 | 
						|
  @retval EFI_DEVICE_ERROR  Any err information in the Status Register.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
CheckErrorStatus (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 StatusRegister;
 | 
						|
  UINT8 ErrorRegister;
 | 
						|
 | 
						|
  StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | 
						|
 | 
						|
  DEBUG_CODE_BEGIN ();
 | 
						|
 | 
						|
    if ((StatusRegister & ATA_STSREG_DWF) != 0) {
 | 
						|
      DEBUG (
 | 
						|
        (EFI_D_BLKIO,
 | 
						|
        "CheckErrorStatus()-- %02x : Error : Write Fault\n",
 | 
						|
        StatusRegister)
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    if ((StatusRegister & ATA_STSREG_CORR) != 0) {
 | 
						|
      DEBUG (
 | 
						|
        (EFI_D_BLKIO,
 | 
						|
        "CheckErrorStatus()-- %02x : Error : Corrected Data\n",
 | 
						|
        StatusRegister)
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    if ((StatusRegister & ATA_STSREG_ERR) != 0) {
 | 
						|
      ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_BBK) != 0) {
 | 
						|
      DEBUG (
 | 
						|
        (EFI_D_BLKIO,
 | 
						|
        "CheckErrorStatus()-- %02x : Error : Bad Block Detected\n",
 | 
						|
        ErrorRegister)
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_UNC) != 0) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_BLKIO,
 | 
						|
          "CheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",
 | 
						|
          ErrorRegister)
 | 
						|
          );
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_MC) != 0) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_BLKIO,
 | 
						|
          "CheckErrorStatus()-- %02x : Error : Media Change\n",
 | 
						|
          ErrorRegister)
 | 
						|
          );
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_ABRT) != 0) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_BLKIO,
 | 
						|
          "CheckErrorStatus()-- %02x : Error : Abort\n",
 | 
						|
          ErrorRegister)
 | 
						|
          );
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_TK0NF) != 0) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_BLKIO,
 | 
						|
          "CheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",
 | 
						|
          ErrorRegister)
 | 
						|
          );
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ErrorRegister & ATA_ERRREG_AMNF) != 0) {
 | 
						|
        DEBUG (
 | 
						|
          (EFI_D_BLKIO,
 | 
						|
          "CheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",
 | 
						|
          ErrorRegister)
 | 
						|
          );
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
  DEBUG_CODE_END ();
 | 
						|
 | 
						|
  if ((StatusRegister & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_DEVICE_ERROR;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoReadBlocks() to perform
 | 
						|
  reading from media in block unit.
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer
 | 
						|
  A pointer to the destination buffer for the data.
 | 
						|
 | 
						|
  @param[in] Lba
 | 
						|
  The starting logical block address to read from
 | 
						|
  on the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks
 | 
						|
  The number of transfer data blocks.
 | 
						|
 | 
						|
  @return return status is fully dependent on the return status
 | 
						|
  of AtaPioDataIn() function.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaReadSectors (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         Lba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       BlocksRemaining;
 | 
						|
  UINT32      Lba32;
 | 
						|
  UINT8       Lba0;
 | 
						|
  UINT8       Lba1;
 | 
						|
  UINT8       Lba2;
 | 
						|
  UINT8       Lba3;
 | 
						|
  UINT8       AtaCommand;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT16      SectorCount;
 | 
						|
  UINTN       ByteCount;
 | 
						|
  VOID        *Buffer;
 | 
						|
 | 
						|
  Buffer = DataBuffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // Using ATA Read Sector(s) command (opcode=0x20) with PIO DATA IN protocol
 | 
						|
  //
 | 
						|
  AtaCommand      = ATA_CMD_READ_SECTORS;
 | 
						|
 | 
						|
 | 
						|
  BlocksRemaining = NumberOfBlocks;
 | 
						|
 | 
						|
  Lba32           = (UINT32) Lba;
 | 
						|
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
 | 
						|
  while (BlocksRemaining > 0) {
 | 
						|
 | 
						|
    //
 | 
						|
    // in ATA-3 spec, LBA is in 28 bit width
 | 
						|
    //
 | 
						|
    Lba0  = (UINT8) Lba32;
 | 
						|
    Lba1  = (UINT8) (Lba32 >> 8);
 | 
						|
    Lba2  = (UINT8) (Lba32 >> 16);
 | 
						|
    //
 | 
						|
    // low 4 bit of Lba3 stands for LBA bit24~bit27.
 | 
						|
    //
 | 
						|
    Lba3 = (UINT8) ((Lba32 >> 24) & 0x0f);
 | 
						|
 | 
						|
    if (BlocksRemaining >= 0x100) {
 | 
						|
 | 
						|
      //
 | 
						|
      //  SectorCount8 is sent to Sector Count register, 0x00 means 256
 | 
						|
      //  sectors to be read
 | 
						|
      //
 | 
						|
      SectorCount8 = 0x00;
 | 
						|
      //
 | 
						|
      //  SectorCount is used to record the number of sectors to be read
 | 
						|
      //
 | 
						|
      SectorCount = 256;
 | 
						|
    } else {
 | 
						|
 | 
						|
      SectorCount8  = (UINT8) BlocksRemaining;
 | 
						|
      SectorCount   = (UINT16) BlocksRemaining;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // ByteCount is the number of bytes that will be read
 | 
						|
    //
 | 
						|
    ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
 | 
						|
 | 
						|
    //
 | 
						|
    // call AtaPioDataIn() to send Read Sector Command and receive data read
 | 
						|
    //
 | 
						|
    Status = AtaPioDataIn (
 | 
						|
              IdeDev,
 | 
						|
              Buffer,
 | 
						|
              (UINT32) ByteCount,
 | 
						|
              AtaCommand,
 | 
						|
              Lba3,
 | 
						|
              SectorCount8,
 | 
						|
              Lba0,
 | 
						|
              Lba1,
 | 
						|
              Lba2
 | 
						|
              );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba32 += SectorCount;
 | 
						|
    Buffer = ((UINT8 *) Buffer + ByteCount);
 | 
						|
    BlocksRemaining -= SectorCount;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoWriteBlocks() to perform
 | 
						|
  writing onto media in block unit.
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure,used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *BufferData
 | 
						|
  A pointer to the source buffer for the data.
 | 
						|
 | 
						|
  @param[in] Lba
 | 
						|
  The starting logical block address to write onto
 | 
						|
  the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks
 | 
						|
  The number of transfer data blocks.
 | 
						|
 | 
						|
  @return return status is fully dependent on the return status
 | 
						|
  of AtaPioDataOut() function.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaWriteSectors (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *BufferData,
 | 
						|
  IN  EFI_LBA         Lba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       BlocksRemaining;
 | 
						|
  UINT32      Lba32;
 | 
						|
  UINT8       Lba0;
 | 
						|
  UINT8       Lba1;
 | 
						|
  UINT8       Lba2;
 | 
						|
  UINT8       Lba3;
 | 
						|
  UINT8       AtaCommand;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT16      SectorCount;
 | 
						|
  UINTN       ByteCount;
 | 
						|
  VOID        *Buffer;
 | 
						|
 | 
						|
  Buffer = BufferData;
 | 
						|
 | 
						|
  //
 | 
						|
  // Using Write Sector(s) command (opcode=0x30) with PIO DATA OUT protocol
 | 
						|
  //
 | 
						|
  AtaCommand      = ATA_CMD_WRITE_SECTORS;
 | 
						|
 | 
						|
  BlocksRemaining = NumberOfBlocks;
 | 
						|
 | 
						|
  Lba32           = (UINT32) Lba;
 | 
						|
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
 | 
						|
  while (BlocksRemaining > 0) {
 | 
						|
 | 
						|
    Lba0  = (UINT8) Lba32;
 | 
						|
    Lba1  = (UINT8) (Lba32 >> 8);
 | 
						|
    Lba2  = (UINT8) (Lba32 >> 16);
 | 
						|
    Lba3  = (UINT8) ((Lba32 >> 24) & 0x0f);
 | 
						|
 | 
						|
    if (BlocksRemaining >= 0x100) {
 | 
						|
 | 
						|
      //
 | 
						|
      //  SectorCount8 is sent to Sector Count register, 0x00 means 256 sectors
 | 
						|
      //  to be written
 | 
						|
      //
 | 
						|
      SectorCount8 = 0x00;
 | 
						|
      //
 | 
						|
      //  SectorCount is used to record the number of sectors to be written
 | 
						|
      //
 | 
						|
      SectorCount = 256;
 | 
						|
    } else {
 | 
						|
 | 
						|
      SectorCount8  = (UINT8) BlocksRemaining;
 | 
						|
      SectorCount   = (UINT16) BlocksRemaining;
 | 
						|
    }
 | 
						|
 | 
						|
    ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
 | 
						|
 | 
						|
    Status = AtaPioDataOut (
 | 
						|
              IdeDev,
 | 
						|
              Buffer,
 | 
						|
              (UINT32) ByteCount,
 | 
						|
              AtaCommand,
 | 
						|
              Lba3,
 | 
						|
              SectorCount8,
 | 
						|
              Lba0,
 | 
						|
              Lba1,
 | 
						|
              Lba2
 | 
						|
              );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba32 += SectorCount;
 | 
						|
    Buffer = ((UINT8 *) Buffer + ByteCount);
 | 
						|
    BlocksRemaining -= SectorCount;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is used to implement the Soft Reset on the specified
 | 
						|
  device. But, the ATA Soft Reset mechanism is so strong a reset method
 | 
						|
  that it will force resetting on both devices connected to the
 | 
						|
  same cable.
 | 
						|
 | 
						|
  It is called by IdeBlkIoReset(), a interface function of Block
 | 
						|
  I/O protocol.
 | 
						|
 | 
						|
  This function can also be used by the ATAPI device to perform reset when
 | 
						|
  ATAPI Reset command is failed.
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       Soft reset completes successfully.
 | 
						|
  @retval EFI_DEVICE_ERROR  Any step during the reset process is failed.
 | 
						|
 | 
						|
  @note
 | 
						|
  The registers initial values after ATA soft reset are different
 | 
						|
  to the ATA device and ATAPI device.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaSoftReset (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
 | 
						|
  UINT8 DeviceControl;
 | 
						|
 | 
						|
  DeviceControl = 0;
 | 
						|
  //
 | 
						|
  // set SRST bit to initiate soft reset
 | 
						|
  //
 | 
						|
  DeviceControl |= ATA_CTLREG_SRST;
 | 
						|
 | 
						|
  //
 | 
						|
  // disable Interrupt
 | 
						|
  //
 | 
						|
  DeviceControl |= BIT1;
 | 
						|
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
 | 
						|
 | 
						|
  //
 | 
						|
  // SRST should assert for at least 5 us, we use 10 us for
 | 
						|
  // better compatibility
 | 
						|
  //
 | 
						|
  gBS->Stall (10);
 | 
						|
 | 
						|
  //
 | 
						|
  // Enable interrupt to support UDMA, and clear SRST bit
 | 
						|
  //
 | 
						|
  DeviceControl = 0;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
 | 
						|
 | 
						|
  //
 | 
						|
  // Wait for at least 2 ms to check BSY status, we use 10 ms
 | 
						|
  // for better compatibility
 | 
						|
  //
 | 
						|
  gBS->Stall(10000);
 | 
						|
  //
 | 
						|
  // slave device needs at most 31s to clear BSY
 | 
						|
  //
 | 
						|
  if (WaitForBSYClear (IdeDev, 31000) == EFI_TIMEOUT) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is the ATA implementation for ReadBlocks in the
 | 
						|
  Block I/O Protocol interface.
 | 
						|
 | 
						|
  @param[in] *IdeBlkIoDevice
 | 
						|
  Indicates the calling context.
 | 
						|
 | 
						|
  @param[in] MediaId
 | 
						|
  The media id that the read request is for.
 | 
						|
 | 
						|
  @param[in] LBA
 | 
						|
  The starting logical block address to read from
 | 
						|
  on the device.
 | 
						|
 | 
						|
  @param[in] BufferSize
 | 
						|
  The size of the Buffer in bytes. This 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 either having implicit
 | 
						|
  or explicit ownership of the memory that data is read into.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       Read Blocks successfully.
 | 
						|
  @retval EFI_DEVICE_ERROR  Read Blocks failed.
 | 
						|
  @retval EFI_NO_MEDIA      There is no media in the device.
 | 
						|
  @retval EFI_MEDIA_CHANGE  The MediaId is not for the current media.
 | 
						|
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE
 | 
						|
  The BufferSize parameter is not a multiple of the
 | 
						|
  intrinsic block size of the device.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER
 | 
						|
  The read request contains LBAs that are not valid,
 | 
						|
  or the data buffer is not valid.
 | 
						|
 | 
						|
  @note
 | 
						|
  If Read Block error because of device error, this function will call
 | 
						|
  AtaSoftReset() function to reset device.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaBlkIoReadBlocks (
 | 
						|
  IN IDE_BLK_IO_DEV   *IdeBlkIoDevice,
 | 
						|
  IN UINT32           MediaId,
 | 
						|
  IN EFI_LBA          LBA,
 | 
						|
  IN UINTN            BufferSize,
 | 
						|
  OUT VOID            *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_BLOCK_IO_MEDIA  *Media;
 | 
						|
  UINTN               BlockSize;
 | 
						|
  UINTN               NumberOfBlocks;
 | 
						|
  EFI_STATUS          Status;
 | 
						|
 | 
						|
  if (Buffer == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (BufferSize == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Get the intrinsic block size
 | 
						|
  //
 | 
						|
  Media           = IdeBlkIoDevice->BlkIo.Media;
 | 
						|
  BlockSize       = Media->BlockSize;
 | 
						|
 | 
						|
  NumberOfBlocks  = BufferSize / BlockSize;
 | 
						|
 | 
						|
  if (MediaId != Media->MediaId) {
 | 
						|
    return EFI_MEDIA_CHANGED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (BufferSize % BlockSize != 0) {
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(Media->MediaPresent)) {
 | 
						|
    return EFI_NO_MEDIA;
 | 
						|
  }
 | 
						|
 | 
						|
  if (LBA > Media->LastBlock) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
 | 
						|
    //
 | 
						|
    // For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 read block mechanism
 | 
						|
    //
 | 
						|
    if (IdeBlkIoDevice->UdmaMode.Valid) {
 | 
						|
      Status = AtaUdmaReadExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    } else {
 | 
						|
      Status = AtaReadSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // For ATA-3 compatible device, use ATA-3 read block mechanism
 | 
						|
    //
 | 
						|
    if (IdeBlkIoDevice->UdmaMode.Valid) {
 | 
						|
      Status = AtaUdmaRead (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    } else {
 | 
						|
      Status = AtaReadSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    AtaSoftReset (IdeBlkIoDevice);
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is the ATA implementation for WriteBlocks in the
 | 
						|
  Block I/O Protocol interface.
 | 
						|
 | 
						|
  @param[in] *IdeBlkIoDevice
 | 
						|
  Indicates the calling context.
 | 
						|
 | 
						|
  @param[in] MediaId
 | 
						|
  The media id that the write request is for.
 | 
						|
 | 
						|
  @param[in] LBA
 | 
						|
  The starting logical block address to write onto
 | 
						|
  the device.
 | 
						|
 | 
						|
  @param[in] BufferSize
 | 
						|
  The size of the Buffer in bytes. This must be a
 | 
						|
  multiple of the intrinsic block size of the device.
 | 
						|
 | 
						|
  @param[out] *Buffer
 | 
						|
  A pointer to the source buffer for the data.
 | 
						|
  The caller is responsible for either having implicit
 | 
						|
  or explicit ownership of the memory that data is
 | 
						|
  written from.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       Write Blocks successfully.
 | 
						|
  @retval EFI_DEVICE_ERROR  Write Blocks failed.
 | 
						|
  @retval EFI_NO_MEDIA      There is no media in the device.
 | 
						|
  @retval EFI_MEDIA_CHANGE  The MediaId is not for the current media.
 | 
						|
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE
 | 
						|
  The BufferSize parameter is not a multiple of the
 | 
						|
  intrinsic block size of the device.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER
 | 
						|
  The write request contains LBAs that are not valid,
 | 
						|
  or the data buffer is not valid.
 | 
						|
 | 
						|
  @note
 | 
						|
  If Write Block error because of device error, this function will call
 | 
						|
  AtaSoftReset() function to reset device.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaBlkIoWriteBlocks (
 | 
						|
  IN  IDE_BLK_IO_DEV   *IdeBlkIoDevice,
 | 
						|
  IN  UINT32           MediaId,
 | 
						|
  IN  EFI_LBA          LBA,
 | 
						|
  IN  UINTN            BufferSize,
 | 
						|
  OUT VOID             *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
 | 
						|
  EFI_BLOCK_IO_MEDIA  *Media;
 | 
						|
  UINTN               BlockSize;
 | 
						|
  UINTN               NumberOfBlocks;
 | 
						|
  EFI_STATUS          Status;
 | 
						|
 | 
						|
  if (Buffer == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (BufferSize == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the intrinsic block size
 | 
						|
  //
 | 
						|
  Media           = IdeBlkIoDevice->BlkIo.Media;
 | 
						|
  BlockSize       = Media->BlockSize;
 | 
						|
  NumberOfBlocks  = BufferSize / BlockSize;
 | 
						|
 | 
						|
  if (MediaId != Media->MediaId) {
 | 
						|
    return EFI_MEDIA_CHANGED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (BufferSize % BlockSize != 0) {
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (LBA > Media->LastBlock) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
 | 
						|
    //
 | 
						|
    // For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 write block mechanism
 | 
						|
    //
 | 
						|
    if (IdeBlkIoDevice->UdmaMode.Valid) {
 | 
						|
      Status = AtaUdmaWriteExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    } else {
 | 
						|
      Status = AtaWriteSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // For ATA-3 compatible device, use ATA-3 write block mechanism
 | 
						|
    //
 | 
						|
    if (IdeBlkIoDevice->UdmaMode.Valid) {
 | 
						|
      Status = AtaUdmaWrite (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    } else {
 | 
						|
      Status = AtaWriteSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    AtaSoftReset (IdeBlkIoDevice);
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoReadBlocks() to perform
 | 
						|
  reading from media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer    A pointer to the destination buffer for the data.
 | 
						|
  @param[in] StartLba       The starting logical block address to read from
 | 
						|
  on the device media.
 | 
						|
  @param[in] NumberOfBlocks The number of transfer data blocks.
 | 
						|
 | 
						|
  @return return status is fully dependent on the return status
 | 
						|
  of AtaPioDataInExt() function.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaReadSectorsExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       BlocksRemaining;
 | 
						|
  EFI_LBA     Lba64;
 | 
						|
  UINT8       AtaCommand;
 | 
						|
  UINT16      SectorCount;
 | 
						|
  UINT32      ByteCount;
 | 
						|
  VOID        *Buffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // Using ATA "Read Sectors Ext" command(opcode=0x24) with PIO DATA IN protocol
 | 
						|
  //
 | 
						|
  AtaCommand      = ATA_CMD_READ_SECTORS_EXT;
 | 
						|
  Buffer          = DataBuffer;
 | 
						|
  BlocksRemaining = NumberOfBlocks;
 | 
						|
  Lba64           = StartLba;
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
 | 
						|
  while (BlocksRemaining > 0) {
 | 
						|
 | 
						|
    if (BlocksRemaining >= 0x10000) {
 | 
						|
      //
 | 
						|
      //  SectorCount is used to record the number of sectors to be read
 | 
						|
      //  Max 65536 sectors can be transfered at a time.
 | 
						|
      //
 | 
						|
      SectorCount = 0xffff;
 | 
						|
    } else {
 | 
						|
      SectorCount = (UINT16) BlocksRemaining;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // ByteCount is the number of bytes that will be read
 | 
						|
    //
 | 
						|
    ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
 | 
						|
 | 
						|
    //
 | 
						|
    // call AtaPioDataInExt() to send Read Sector Command and receive data read
 | 
						|
    //
 | 
						|
    Status = AtaPioDataInExt (
 | 
						|
              IdeDev,
 | 
						|
              Buffer,
 | 
						|
              ByteCount,
 | 
						|
              AtaCommand,
 | 
						|
              Lba64,
 | 
						|
              SectorCount
 | 
						|
              );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba64 += SectorCount;
 | 
						|
    Buffer = ((UINT8 *) Buffer + ByteCount);
 | 
						|
    BlocksRemaining -= SectorCount;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoWriteBlocks() to perform
 | 
						|
  writing onto media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure,used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer
 | 
						|
  A pointer to the source buffer for the data.
 | 
						|
 | 
						|
  @param[in] StartLba
 | 
						|
  The starting logical block address to write onto
 | 
						|
  the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks
 | 
						|
  The number of transfer data blocks.
 | 
						|
 | 
						|
  @return status is fully dependent on the return status
 | 
						|
  of AtaPioDataOutExt() function.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaWriteSectorsExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  EFI_LBA     Lba64;
 | 
						|
  UINTN       BlocksRemaining;
 | 
						|
  UINT8       AtaCommand;
 | 
						|
  UINT16      SectorCount;
 | 
						|
  UINT32      ByteCount;
 | 
						|
  VOID        *Buffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // Using ATA "Write Sectors Ext" cmd(opcode=0x24) with PIO DATA OUT protocol
 | 
						|
  //
 | 
						|
  AtaCommand      = ATA_CMD_WRITE_SECTORS_EXT;
 | 
						|
  Lba64           = StartLba;
 | 
						|
  Buffer          = DataBuffer;
 | 
						|
  BlocksRemaining = NumberOfBlocks;
 | 
						|
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
 | 
						|
  while (BlocksRemaining > 0) {
 | 
						|
 | 
						|
    if (BlocksRemaining >= 0x10000) {
 | 
						|
      //
 | 
						|
      //  SectorCount is used to record the number of sectors to be written.
 | 
						|
      //  Max 65536 sectors can be transfered at a time.
 | 
						|
      //
 | 
						|
      SectorCount = 0xffff;
 | 
						|
    } else {
 | 
						|
      SectorCount = (UINT16) BlocksRemaining;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // ByteCount is the number of bytes that will be written
 | 
						|
    //
 | 
						|
    ByteCount = SectorCount * (IdeDev->BlkIo.Media->BlockSize);
 | 
						|
 | 
						|
    //
 | 
						|
    // Call AtaPioDataOutExt() to send "Write Sectors Ext" Command
 | 
						|
    //
 | 
						|
    Status = AtaPioDataOutExt (
 | 
						|
              IdeDev,
 | 
						|
              Buffer,
 | 
						|
              ByteCount,
 | 
						|
              AtaCommand,
 | 
						|
              Lba64,
 | 
						|
              SectorCount
 | 
						|
              );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba64 += SectorCount;
 | 
						|
    Buffer = ((UINT8 *) Buffer + ByteCount);
 | 
						|
    BlocksRemaining -= SectorCount;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is used to send out ATA commands conforms to the
 | 
						|
  PIO Data In Protocol, supporting ATA/ATAPI-6 standard
 | 
						|
 | 
						|
  Comparing with ATA-3 data in protocol, we have two differents here:<BR>
 | 
						|
  1. Do NOT wait for DRQ clear before sending command into IDE device.(the
 | 
						|
  wait will frequently fail... cause writing function return error)
 | 
						|
 | 
						|
  2. Do NOT wait for DRQ clear after all data readed.(the wait greatly
 | 
						|
  slow down writing performance by 100 times!)
 | 
						|
 | 
						|
  @param[in] *IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in, out] *Buffer  buffer contained data transferred from device to host.
 | 
						|
  @param[in] ByteCount    data size in byte unit of the buffer.
 | 
						|
  @param[in] AtaCommand   value of the Command Register
 | 
						|
  @param[in] StartLba     the start LBA of this transaction
 | 
						|
  @param[in] SectorCount  the count of sectors to be transfered
 | 
						|
 | 
						|
  @retval EFI_SUCCESS send out the ATA command and device send required
 | 
						|
  data successfully.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR command sent failed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaPioDataInExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  OUT VOID        *Buffer,
 | 
						|
  IN  UINT32          ByteCount,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINT16          SectorCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8       DevSel;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT8       LbaLow;
 | 
						|
  UINT8       LbaMid;
 | 
						|
  UINT8       LbaHigh;
 | 
						|
  UINTN       WordCount;
 | 
						|
  UINTN       Increment;
 | 
						|
  UINT16      *Buffer16;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Select device, set bit6 as 1 to indicate LBA mode is used
 | 
						|
  //
 | 
						|
  DevSel = (UINT8) (IdeDev->Device << 4);
 | 
						|
  DevSel |= 0x40;
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    DevSel
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // Wait for DRDY singnal asserting. ATAPI device needn't wait
 | 
						|
  //
 | 
						|
  if ( (IdeDev->Type == IdeHardDisk)  ||
 | 
						|
        (IdeDev->Type == Ide48bitAddressingHardDisk)) {
 | 
						|
 | 
						|
    Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill feature register if needed
 | 
						|
  //
 | 
						|
  if (AtaCommand == ATA_CMD_SET_FEATURES) {
 | 
						|
    IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the sector count register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  SectorCount8 = (UINT8) (SectorCount >> 8);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  SectorCount8 = (UINT8) SectorCount;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the start LBA registers, which are also two-byte FIFO
 | 
						|
  //
 | 
						|
  LbaLow  = (UINT8) RShiftU64 (StartLba, 24);
 | 
						|
  LbaMid  = (UINT8) RShiftU64 (StartLba, 32);
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (StartLba, 40);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
 | 
						|
  LbaLow  = (UINT8) StartLba;
 | 
						|
  LbaMid  = (UINT8) RShiftU64 (StartLba, 8);
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (StartLba, 16);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
 | 
						|
  //
 | 
						|
  // Send command via Command Register, invoking the processing of this command
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  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;
 | 
						|
  //
 | 
						|
 | 
						|
  //
 | 
						|
  // 256 words
 | 
						|
  //
 | 
						|
  Increment = 256;
 | 
						|
 | 
						|
  //
 | 
						|
  // used to record bytes of currently transfered data
 | 
						|
  //
 | 
						|
  WordCount = 0;
 | 
						|
 | 
						|
  while (WordCount < ByteCount / 2) {
 | 
						|
    //
 | 
						|
    // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
 | 
						|
    //
 | 
						|
    Status = DRQReady2 (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = CheckErrorStatus (IdeDev);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Get the byte count for one series of read
 | 
						|
    //
 | 
						|
    if ((WordCount + Increment) > ByteCount / 2) {
 | 
						|
      Increment = ByteCount / 2 - WordCount;
 | 
						|
    }
 | 
						|
 | 
						|
    IDEReadPortWMultiple (
 | 
						|
      IdeDev->PciIo,
 | 
						|
      IdeDev->IoPort->Data,
 | 
						|
      Increment,
 | 
						|
      Buffer16
 | 
						|
      );
 | 
						|
 | 
						|
    WordCount += Increment;
 | 
						|
    Buffer16 += Increment;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  return CheckErrorStatus (IdeDev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is used to send out ATA commands conforms to the
 | 
						|
  PIO Data Out Protocol, supporting ATA/ATAPI-6 standard
 | 
						|
 | 
						|
  Comparing with ATA-3 data out protocol, we have two differents here:<BR>
 | 
						|
  1. Do NOT wait for DRQ clear before sending command into IDE device.(the
 | 
						|
  wait will frequently fail... cause writing function return error)
 | 
						|
 | 
						|
  2. Do NOT wait for DRQ clear after all data readed.(the wait greatly
 | 
						|
  slow down writing performance by 100 times!)
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *Buffer      buffer contained data transferred from host to device.
 | 
						|
  @param[in] ByteCount    data size in byte unit of the buffer.
 | 
						|
  @param[in] AtaCommand   value of the Command Register
 | 
						|
  @param[in] StartLba     the start LBA of this transaction
 | 
						|
  @param[in] SectorCount  the count of sectors to be transfered
 | 
						|
 | 
						|
  @retval EFI_SUCCESS send out the ATA command and device receive required
 | 
						|
  data successfully.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR command sent failed.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaPioDataOutExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *Buffer,
 | 
						|
  IN  UINT32          ByteCount,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINT16          SectorCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8       DevSel;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT8       LbaLow;
 | 
						|
  UINT8       LbaMid;
 | 
						|
  UINT8       LbaHigh;
 | 
						|
  UINTN       WordCount;
 | 
						|
  UINTN       Increment;
 | 
						|
  UINT16      *Buffer16;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Select device. Set bit6 as 1 to indicate LBA mode is used
 | 
						|
  //
 | 
						|
  DevSel = (UINT8) (IdeDev->Device << 4);
 | 
						|
  DevSel |= 0x40;
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    DevSel
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // Wait for DRDY singnal asserting.
 | 
						|
  //
 | 
						|
  Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill feature register if needed
 | 
						|
  //
 | 
						|
  if (AtaCommand == ATA_CMD_SET_FEATURES) {
 | 
						|
    IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x03);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the sector count register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  SectorCount8 = (UINT8) (SectorCount >> 8);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  SectorCount8 = (UINT8) SectorCount;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the start LBA registers, which are also two-byte FIFO
 | 
						|
  //
 | 
						|
  LbaLow  = (UINT8) RShiftU64 (StartLba, 24);
 | 
						|
  LbaMid  = (UINT8) RShiftU64 (StartLba, 32);
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (StartLba, 40);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
 | 
						|
  LbaLow  = (UINT8) StartLba;
 | 
						|
  LbaMid  = (UINT8) RShiftU64 (StartLba, 8);
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (StartLba, 16);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
 | 
						|
  //
 | 
						|
  // Send command via Command Register, invoking the processing of this command
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  Buffer16 = (UINT16 *) Buffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // According to PIO Data Out protocol, host can perform a series of writes to
 | 
						|
  // the data register after each time device set DRQ ready;
 | 
						|
  //
 | 
						|
  Increment = 256;
 | 
						|
 | 
						|
  //
 | 
						|
  // used to record bytes of currently transfered data
 | 
						|
  //
 | 
						|
  WordCount = 0;
 | 
						|
 | 
						|
  while (WordCount < ByteCount / 2) {
 | 
						|
    //
 | 
						|
    // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
 | 
						|
    //
 | 
						|
    Status = DRQReady2 (IdeDev, ATATIMEOUT);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = CheckErrorStatus (IdeDev);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Write data into device by one series of writing to data register
 | 
						|
    //
 | 
						|
    if ((WordCount + Increment) > ByteCount / 2) {
 | 
						|
      Increment = ByteCount / 2 - WordCount;
 | 
						|
    }
 | 
						|
 | 
						|
    IDEWritePortWMultiple (
 | 
						|
      IdeDev->PciIo,
 | 
						|
      IdeDev->IoPort->Data,
 | 
						|
      Increment,
 | 
						|
      Buffer16
 | 
						|
      );
 | 
						|
 | 
						|
    WordCount += Increment;
 | 
						|
    Buffer16 += Increment;
 | 
						|
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // while
 | 
						|
  //
 | 
						|
 | 
						|
  return CheckErrorStatus (IdeDev);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Enable SMART of the disk if supported
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure,used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
AtaSMARTSupport (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS        Status;
 | 
						|
  BOOLEAN           SMARTSupported;
 | 
						|
  UINT8             Device;
 | 
						|
  EFI_IDENTIFY_DATA *TmpAtaIdentifyPointer;
 | 
						|
  UINT8             DeviceSelect;
 | 
						|
  UINT8             LBAMid;
 | 
						|
  UINT8             LBAHigh;
 | 
						|
 | 
						|
  //
 | 
						|
  // Detect if the device supports S.M.A.R.T.
 | 
						|
  //
 | 
						|
  if ((IdeDev->pIdData->AtaData.command_set_supported_83 & 0xc000) != 0x4000) {
 | 
						|
    //
 | 
						|
    // Data in word 82 is not valid (bit15 shall be zero and bit14 shall be to one)
 | 
						|
    //
 | 
						|
    return ;
 | 
						|
  } else {
 | 
						|
    if ((IdeDev->pIdData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {
 | 
						|
      //
 | 
						|
      // S.M.A.R.T is not supported by the device
 | 
						|
      //
 | 
						|
      SMARTSupported = FALSE;
 | 
						|
    } else {
 | 
						|
      SMARTSupported = TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!SMARTSupported) {
 | 
						|
    //
 | 
						|
    // Report nonsupport status code
 | 
						|
    //
 | 
						|
    REPORT_STATUS_CODE (
 | 
						|
      EFI_ERROR_CODE | EFI_ERROR_MINOR,
 | 
						|
      (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)
 | 
						|
      );
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Enable this feature
 | 
						|
    //
 | 
						|
    REPORT_STATUS_CODE (
 | 
						|
      EFI_PROGRESS_CODE,
 | 
						|
      (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)
 | 
						|
      );
 | 
						|
 | 
						|
    Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
 | 
						|
    Status = AtaNonDataCommandIn (
 | 
						|
              IdeDev,
 | 
						|
              ATA_CMD_SMART,
 | 
						|
              Device,
 | 
						|
              ATA_SMART_ENABLE_OPERATION,
 | 
						|
              0,
 | 
						|
              0,
 | 
						|
              ATA_CONSTANT_4F,
 | 
						|
              ATA_CONSTANT_C2
 | 
						|
              );
 | 
						|
    //
 | 
						|
    // Detect if this feature is enabled
 | 
						|
    //
 | 
						|
    TmpAtaIdentifyPointer = (EFI_IDENTIFY_DATA *) AllocateZeroPool (sizeof (EFI_IDENTIFY_DATA));
 | 
						|
    if (TmpAtaIdentifyPointer == NULL) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    DeviceSelect          = (UINT8) ((IdeDev->Device) << 4);
 | 
						|
    Status = AtaPioDataIn (
 | 
						|
              IdeDev,
 | 
						|
              (VOID *) TmpAtaIdentifyPointer,
 | 
						|
              sizeof (EFI_IDENTIFY_DATA),
 | 
						|
              ATA_CMD_IDENTIFY_DRIVE,
 | 
						|
              DeviceSelect,
 | 
						|
              0,
 | 
						|
              0,
 | 
						|
              0,
 | 
						|
              0
 | 
						|
              );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      gBS->FreePool (TmpAtaIdentifyPointer);
 | 
						|
      return ;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Check if the feature is enabled
 | 
						|
    //
 | 
						|
    if ((TmpAtaIdentifyPointer->AtaData.command_set_feature_enb_85 & 0x0001) == 0x0001) {
 | 
						|
      //
 | 
						|
      // Read status data
 | 
						|
      //
 | 
						|
      AtaNonDataCommandIn (
 | 
						|
        IdeDev,
 | 
						|
        ATA_CMD_SMART,
 | 
						|
        Device,
 | 
						|
        ATA_SMART_RETURN_STATUS,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        ATA_CONSTANT_4F,
 | 
						|
        ATA_CONSTANT_C2
 | 
						|
        );
 | 
						|
      LBAMid  = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb);
 | 
						|
      LBAHigh = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb);
 | 
						|
 | 
						|
      if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {
 | 
						|
        //
 | 
						|
        // The threshold exceeded condition is not detected by the device
 | 
						|
        //
 | 
						|
        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
 | 
						|
        //
 | 
						|
        REPORT_STATUS_CODE (
 | 
						|
              EFI_PROGRESS_CODE,
 | 
						|
              (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)
 | 
						|
              );
 | 
						|
      }
 | 
						|
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Report disabled status code
 | 
						|
      //
 | 
						|
      REPORT_STATUS_CODE (
 | 
						|
            EFI_ERROR_CODE | EFI_ERROR_MINOR,
 | 
						|
            (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)
 | 
						|
            );
 | 
						|
    }
 | 
						|
 | 
						|
    gBS->FreePool (TmpAtaIdentifyPointer);
 | 
						|
  }
 | 
						|
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Enable Long Physical Sector Feature for ATA device.
 | 
						|
 | 
						|
  @param   IdeDev  The IDE device data
 | 
						|
 | 
						|
  @retval  EFI_SUCCESS      The ATA device supports Long Physical Sector feature
 | 
						|
                            and corresponding fields in BlockIo structure is updated.
 | 
						|
  @retval  EFI_UNSUPPORTED  The device is not ATA device or Long Physical Sector
 | 
						|
                            feature is not supported.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaEnableLongPhysicalSector (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_ATA_IDENTIFY_DATA  *AtaIdentifyData;
 | 
						|
  UINT16                 PhyLogicSectorSupport;
 | 
						|
 | 
						|
  ASSERT (IdeDev->pIdData != NULL);
 | 
						|
  //
 | 
						|
  // Only valid for ATA device
 | 
						|
  //
 | 
						|
  AtaIdentifyData       = (EFI_ATA_IDENTIFY_DATA *) &IdeDev->pIdData->AtaData;
 | 
						|
  if (AtaIdentifyData->config & 0x8000) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
  PhyLogicSectorSupport = AtaIdentifyData->phy_logic_sector_support;
 | 
						|
  //
 | 
						|
  // Check whether Long Physical Sector Feature is supported
 | 
						|
  //
 | 
						|
  if ((PhyLogicSectorSupport & 0xc000) == 0x4000) {
 | 
						|
    IdeDev->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 1;
 | 
						|
    IdeDev->BlkIo.Media->LowestAlignedLba              = 0;
 | 
						|
    //
 | 
						|
    // Check whether one physical block contains multiple physical blocks
 | 
						|
    //
 | 
						|
    if (PhyLogicSectorSupport & 0x2000) {
 | 
						|
      IdeDev->BlkIo.Media->LogicalBlocksPerPhysicalBlock =
 | 
						|
        (UINT32) (1 << (PhyLogicSectorSupport & 0x000f));
 | 
						|
      //
 | 
						|
      // Check lowest alignment of logical blocks within physical block
 | 
						|
      //
 | 
						|
      if ((AtaIdentifyData->alignment_logic_in_phy_blocks & 0xc000) == 0x4000) {
 | 
						|
        IdeDev->BlkIo.Media->LowestAlignedLba =
 | 
						|
          (EFI_LBA) (AtaIdentifyData->alignment_logic_in_phy_blocks & 0x3fff);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Check logical block size
 | 
						|
    //
 | 
						|
    IdeDev->BlkIo.Media->BlockSize = 0x200;
 | 
						|
    if (PhyLogicSectorSupport & 0x1000) {
 | 
						|
      IdeDev->BlkIo.Media->BlockSize = (UINT32) (
 | 
						|
        ((AtaIdentifyData->logic_sector_size_hi << 16) |
 | 
						|
         AtaIdentifyData->logic_sector_size_lo) * sizeof (UINT16)
 | 
						|
        );
 | 
						|
    }
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  } else {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Send ATA Ext command into device with NON_DATA protocol
 | 
						|
 | 
						|
  @param  IdeDev Standard IDE device private data structure
 | 
						|
  @param  AtaCommand The ATA command to be sent
 | 
						|
  @param  Device The value in Device register
 | 
						|
  @param  Feature The value in Feature register
 | 
						|
  @param  SectorCount The value in SectorCount register
 | 
						|
  @param  LbaAddress The LBA address in 48-bit mode
 | 
						|
 | 
						|
  @retval  EFI_SUCCESS Reading succeed
 | 
						|
  @retval  EFI_DEVICE_ERROR Error executing commands on this device.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaCommandIssueExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  UINT8           Device,
 | 
						|
  IN  UINT16          Feature,
 | 
						|
  IN  UINT16          SectorCount,
 | 
						|
  IN  EFI_LBA         LbaAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT8       Feature8;
 | 
						|
  UINT8       LbaLow;
 | 
						|
  UINT8       LbaMid;
 | 
						|
  UINT8       LbaHigh;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)
 | 
						|
  //
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    (UINT8) ((IdeDev->Device << 4) | 0xe0)
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // ATA commands for ATA device must be issued when DRDY is set
 | 
						|
  //
 | 
						|
  Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Pass parameter into device register block
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the feature register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  Feature8 = (UINT8) (Feature >> 8);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature8);
 | 
						|
 | 
						|
  Feature8 = (UINT8) Feature;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the sector count register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  SectorCount8 = (UINT8) (SectorCount >> 8);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  SectorCount8 = (UINT8) SectorCount;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the start LBA registers, which are also two-byte FIFO
 | 
						|
  //
 | 
						|
  LbaLow = (UINT8) RShiftU64 (LbaAddress, 24);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
  LbaLow = (UINT8) LbaAddress;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, LbaLow);
 | 
						|
 | 
						|
  LbaMid = (UINT8) RShiftU64 (LbaAddress, 32);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
  LbaMid = (UINT8) RShiftU64 (LbaAddress, 8);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, LbaMid);
 | 
						|
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (LbaAddress, 40);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
  LbaHigh = (UINT8) RShiftU64 (LbaAddress, 16);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, LbaHigh);
 | 
						|
 | 
						|
  //
 | 
						|
  // Work around for Segate 160G disk writing
 | 
						|
  //
 | 
						|
  gBS->Stall (1800);
 | 
						|
 | 
						|
  //
 | 
						|
  // Send command via Command Register
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  //
 | 
						|
  // Stall at least 400ns
 | 
						|
  //
 | 
						|
  gBS->Stall (100);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Send ATA Ext command into device with NON_DATA protocol
 | 
						|
 | 
						|
  @param  IdeDev Standard IDE device private data structure
 | 
						|
  @param  AtaCommand The ATA command to be sent
 | 
						|
  @param  Device The value in Device register
 | 
						|
  @param  Feature The value in Feature register
 | 
						|
  @param  SectorCount The value in SectorCount register
 | 
						|
  @param  LbaAddress The LBA address in 48-bit mode
 | 
						|
 | 
						|
  @retval  EFI_SUCCESS Reading succeed
 | 
						|
  @retval  EFI_DEVICE_ERROR Error executing commands on this device.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaCommandIssue (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  UINT8           AtaCommand,
 | 
						|
  IN  UINT8           Device,
 | 
						|
  IN  UINT16          Feature,
 | 
						|
  IN  UINT16          SectorCount,
 | 
						|
  IN  EFI_LBA         LbaAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINT8       SectorCount8;
 | 
						|
  UINT8       Feature8;
 | 
						|
  UINT8       Lba0;
 | 
						|
  UINT8       Lba1;
 | 
						|
  UINT8       Lba2;
 | 
						|
  UINT8       Lba3;
 | 
						|
 | 
						|
  Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)
 | 
						|
  //
 | 
						|
  IDEWritePortB (
 | 
						|
    IdeDev->PciIo,
 | 
						|
    IdeDev->IoPort->Head,
 | 
						|
    (UINT8) ((IdeDev->Device << 4) | 0xe0)
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // ATA commands for ATA device must be issued when DRDY is set
 | 
						|
  //
 | 
						|
  Status = DRDYReady (IdeDev, ATATIMEOUT);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  Lba0  = (UINT8) LbaAddress;
 | 
						|
  Lba1  = (UINT8) RShiftU64 (LbaAddress, 8);
 | 
						|
  Lba2  = (UINT8) RShiftU64 (LbaAddress, 16);
 | 
						|
  Lba3  = (UINT8) RShiftU64 (LbaAddress, 24);
 | 
						|
  Device = (UINT8) (Device | Lba3);
 | 
						|
 | 
						|
  //
 | 
						|
  // Pass parameter into device register block
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the feature register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  Feature8 = (UINT8) Feature;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, Feature8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the sector count register, which is a two-byte FIFO. Need write twice.
 | 
						|
  //
 | 
						|
  SectorCount8 = (UINT8) SectorCount;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorCount, SectorCount8);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the start LBA registers, which are also two-byte FIFO
 | 
						|
  //
 | 
						|
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->SectorNumber, Lba0);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb, Lba1);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb, Lba2);
 | 
						|
 | 
						|
  //
 | 
						|
  // Send command via Command Register
 | 
						|
  //
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, AtaCommand);
 | 
						|
 | 
						|
  //
 | 
						|
  // Stall at least 400ns
 | 
						|
  //
 | 
						|
  gBS->Stall (100);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoReadBlocks() to perform
 | 
						|
  reading from media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer A pointer to the destination buffer for the data.
 | 
						|
 | 
						|
  @param[in] StartLba The starting logical block address to read from
 | 
						|
  on the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks The number of transfer data blocks.
 | 
						|
 | 
						|
  @return The device status of UDMA operation. If the operation is
 | 
						|
  successful, return EFI_SUCCESS.
 | 
						|
 | 
						|
  TODO:    EFI_UNSUPPORTED - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaUdmaReadExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  return DoAtaUdma (IdeDev, DataBuffer, StartLba, NumberOfBlocks, AtaUdmaReadExtOp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoReadBlocks() to perform
 | 
						|
  reading from media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer    A pointer to the destination buffer for the data.
 | 
						|
  @param[in] StartLba       The starting logical block address to read from
 | 
						|
  on the device media.
 | 
						|
  @param[in] NumberOfBlocks The number of transfer data blocks.
 | 
						|
 | 
						|
  @return The device status of UDMA operation. If the operation is
 | 
						|
  successful, return EFI_SUCCESS.
 | 
						|
 | 
						|
  TODO:    EFI_UNSUPPORTED - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaUdmaRead (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  return DoAtaUdma (IdeDev, DataBuffer, StartLba, NumberOfBlocks, AtaUdmaReadOp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoWriteBlocks() to perform
 | 
						|
  writing to media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer A pointer to the source buffer for the data.
 | 
						|
 | 
						|
  @param[in] StartLba The starting logical block address to write to
 | 
						|
  on the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks The number of transfer data blocks.
 | 
						|
 | 
						|
  @return The device status of UDMA operation. If the operation is
 | 
						|
  successful, return EFI_SUCCESS.
 | 
						|
 | 
						|
  TODO:    EFI_UNSUPPORTED - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaUdmaWriteExt (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  return DoAtaUdma (IdeDev, DataBuffer, StartLba, NumberOfBlocks, AtaUdmaWriteExtOp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function is called by the AtaBlkIoWriteBlocks() to perform
 | 
						|
  writing to media in block unit. The function has been enhanced to
 | 
						|
  support >120GB access and transfer at most 65536 blocks per command
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer
 | 
						|
  A pointer to the source buffer for the data.
 | 
						|
 | 
						|
  @param[in] StartLba
 | 
						|
  The starting logical block address to write to
 | 
						|
  on the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks
 | 
						|
  The number of transfer data blocks.
 | 
						|
 | 
						|
  @return The device status of UDMA operation. If the operation is
 | 
						|
  successful, return EFI_SUCCESS.
 | 
						|
 | 
						|
  TODO:    EFI_UNSUPPORTED - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
  TODO:    EFI_DEVICE_ERROR - add return value to function comment
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AtaUdmaWrite (
 | 
						|
  IN  IDE_BLK_IO_DEV  *IdeDev,
 | 
						|
  IN  VOID            *DataBuffer,
 | 
						|
  IN  EFI_LBA         StartLba,
 | 
						|
  IN  UINTN           NumberOfBlocks
 | 
						|
  )
 | 
						|
{
 | 
						|
  return DoAtaUdma (IdeDev, DataBuffer, StartLba, NumberOfBlocks, AtaUdmaWriteOp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Perform an ATA Udma operation (Read, ReadExt, Write, WriteExt).
 | 
						|
 | 
						|
  @param[in] *IdeDev
 | 
						|
  pointer pointing to IDE_BLK_IO_DEV data structure, used
 | 
						|
  to record all the information of the IDE device.
 | 
						|
 | 
						|
  @param[in] *DataBuffer
 | 
						|
  A pointer to the source buffer for the data.
 | 
						|
 | 
						|
  @param[in] StartLba
 | 
						|
  The starting logical block address to write to
 | 
						|
  on the device media.
 | 
						|
 | 
						|
  @param[in] NumberOfBlocks
 | 
						|
  The number of transfer data blocks.
 | 
						|
 | 
						|
  @param[in] UdmaOp
 | 
						|
  The perform operations could be AtaUdmaReadOp, AtaUdmaReadExOp,
 | 
						|
  AtaUdmaWriteOp, AtaUdmaWriteExOp
 | 
						|
 | 
						|
  @return The device status of UDMA operation. If the operation is
 | 
						|
  successful, return EFI_SUCCESS.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
DoAtaUdma (
 | 
						|
  IN  IDE_BLK_IO_DEV      *IdeDev,
 | 
						|
  IN  VOID                *DataBuffer,
 | 
						|
  IN  EFI_LBA             StartLba,
 | 
						|
  IN  UINTN               NumberOfBlocks,
 | 
						|
  IN  ATA_UDMA_OPERATION  UdmaOp
 | 
						|
  )
 | 
						|
{
 | 
						|
  IDE_DMA_PRD                   *PrdAddr;
 | 
						|
  IDE_DMA_PRD                   *UsedPrdAddr;
 | 
						|
  IDE_DMA_PRD                   *TempPrdAddr;
 | 
						|
  UINT8                         RegisterValue;
 | 
						|
  UINT8                         Device;
 | 
						|
  UINT64                        IoPortForBmic;
 | 
						|
  UINT64                        IoPortForBmis;
 | 
						|
  UINT64                        IoPortForBmid;
 | 
						|
  EFI_STATUS                    Status;
 | 
						|
  UINTN                         PrdTableNum;
 | 
						|
  UINTN                         ByteCount;
 | 
						|
  UINTN                         ByteAvailable;
 | 
						|
  UINT8                         *PrdBuffer;
 | 
						|
  UINTN                         RemainBlockNum;
 | 
						|
  UINT8                         DeviceControl;
 | 
						|
  UINT32                        Count;
 | 
						|
  UINTN                         PageCount;
 | 
						|
  VOID                          *Map;
 | 
						|
  VOID                          *MemPage;
 | 
						|
  EFI_PHYSICAL_ADDRESS          DeviceAddress;
 | 
						|
  UINTN                         MaxDmaCommandSectors;
 | 
						|
  EFI_PCI_IO_PROTOCOL_OPERATION PciIoProtocolOp;
 | 
						|
  UINT8                         AtaCommand;
 | 
						|
 | 
						|
  switch (UdmaOp) {
 | 
						|
  case AtaUdmaReadOp:
 | 
						|
    MaxDmaCommandSectors = ATAPI_MAX_DMA_CMD_SECTORS;
 | 
						|
    PciIoProtocolOp      = EfiPciIoOperationBusMasterWrite;
 | 
						|
    AtaCommand           = ATA_CMD_READ_DMA;
 | 
						|
    break;
 | 
						|
  case AtaUdmaReadExtOp:
 | 
						|
    MaxDmaCommandSectors = ATAPI_MAX_DMA_EXT_CMD_SECTORS;
 | 
						|
    PciIoProtocolOp      = EfiPciIoOperationBusMasterWrite;
 | 
						|
    AtaCommand           = ATA_CMD_READ_DMA_EXT;
 | 
						|
    break;
 | 
						|
  case AtaUdmaWriteOp:
 | 
						|
    MaxDmaCommandSectors = ATAPI_MAX_DMA_CMD_SECTORS;
 | 
						|
    PciIoProtocolOp      = EfiPciIoOperationBusMasterRead;
 | 
						|
    AtaCommand           = ATA_CMD_WRITE_DMA;
 | 
						|
    break;
 | 
						|
  case AtaUdmaWriteExtOp:
 | 
						|
    MaxDmaCommandSectors = ATAPI_MAX_DMA_EXT_CMD_SECTORS;
 | 
						|
    PciIoProtocolOp      = EfiPciIoOperationBusMasterRead;
 | 
						|
    AtaCommand           = ATA_CMD_WRITE_DMA_EXT;
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Select device
 | 
						|
  //
 | 
						|
  Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
 | 
						|
 | 
						|
  //
 | 
						|
  // Enable interrupt to support UDMA
 | 
						|
  //
 | 
						|
  DeviceControl = 0;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
 | 
						|
 | 
						|
  if (IdePrimary == IdeDev->Channel) {
 | 
						|
    IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICP_OFFSET;
 | 
						|
    IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISP_OFFSET;
 | 
						|
    IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDP_OFFSET;
 | 
						|
  } else {
 | 
						|
    if (IdeSecondary == IdeDev->Channel) {
 | 
						|
      IoPortForBmic = IdeDev->IoPort->BusMasterBaseAddr + BMICS_OFFSET;
 | 
						|
      IoPortForBmis = IdeDev->IoPort->BusMasterBaseAddr + BMISS_OFFSET;
 | 
						|
      IoPortForBmid = IdeDev->IoPort->BusMasterBaseAddr + BMIDS_OFFSET;
 | 
						|
    } else {
 | 
						|
      return EFI_UNSUPPORTED;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Read BMIS register and clear ERROR and INTR bit
 | 
						|
  //
 | 
						|
  IdeDev->PciIo->Io.Read (
 | 
						|
					  IdeDev->PciIo,
 | 
						|
					  EfiPciIoWidthUint8,
 | 
						|
					  EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
					  IoPortForBmis,
 | 
						|
					  1,
 | 
						|
					  &RegisterValue
 | 
						|
					  );
 | 
						|
  
 | 
						|
  RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
 | 
						|
  
 | 
						|
  IdeDev->PciIo->Io.Write (
 | 
						|
					  IdeDev->PciIo,
 | 
						|
					  EfiPciIoWidthUint8,
 | 
						|
					  EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
					  IoPortForBmis,
 | 
						|
					  1,
 | 
						|
					  &RegisterValue
 | 
						|
					  );
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  
 | 
						|
  RemainBlockNum = NumberOfBlocks;
 | 
						|
  while (RemainBlockNum > 0) {
 | 
						|
 | 
						|
    if (RemainBlockNum >= MaxDmaCommandSectors) {
 | 
						|
      //
 | 
						|
      //  SectorCount is used to record the number of sectors to be read
 | 
						|
      //  Max 65536 sectors can be transfered at a time.
 | 
						|
      //
 | 
						|
      NumberOfBlocks = MaxDmaCommandSectors;
 | 
						|
      RemainBlockNum -= MaxDmaCommandSectors;
 | 
						|
    } else {
 | 
						|
      NumberOfBlocks  = (UINT16) RemainBlockNum;
 | 
						|
      RemainBlockNum  = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Calculate the number of PRD table to make sure the memory region
 | 
						|
    // not cross 64K boundary
 | 
						|
    //
 | 
						|
    ByteCount   = NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
 | 
						|
    PrdTableNum = ((ByteCount >> 16) + 1) + 1;
 | 
						|
 | 
						|
    //
 | 
						|
    // Build PRD table
 | 
						|
    //
 | 
						|
    PageCount = EFI_SIZE_TO_PAGES (2 * PrdTableNum * sizeof (IDE_DMA_PRD));
 | 
						|
    Status = IdeDev->PciIo->AllocateBuffer (
 | 
						|
                    IdeDev->PciIo,
 | 
						|
                    AllocateAnyPages,
 | 
						|
                    EfiBootServicesData,
 | 
						|
                    PageCount,
 | 
						|
                    &MemPage,
 | 
						|
                    0
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
    ZeroMem ((VOID *) ((UINTN) MemPage), EFI_PAGES_TO_SIZE (PageCount));
 | 
						|
 | 
						|
    PrdAddr = (IDE_DMA_PRD *) ((UINTN) MemPage);
 | 
						|
    //
 | 
						|
    // To make sure PRD is allocated in one 64K page
 | 
						|
    //
 | 
						|
    if (((UINTN) PrdAddr & 0x0FFFF) > (((UINTN) PrdAddr + PrdTableNum * sizeof (IDE_DMA_PRD) - 1) & 0x0FFFF)) {
 | 
						|
      UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x10000) & 0xFFFF0000);
 | 
						|
    } else {
 | 
						|
      if ((UINTN) PrdAddr & 0x03) {
 | 
						|
        UsedPrdAddr = (IDE_DMA_PRD *) ((UINTN) ((UINT8 *) PrdAddr + 0x04) & 0xFFFFFFFC);
 | 
						|
      } else {
 | 
						|
        UsedPrdAddr = PrdAddr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Build the PRD table
 | 
						|
    //
 | 
						|
    Status = IdeDev->PciIo->Map (
 | 
						|
                       IdeDev->PciIo,
 | 
						|
                       PciIoProtocolOp,
 | 
						|
                       DataBuffer,
 | 
						|
                       &ByteCount,
 | 
						|
                       &DeviceAddress,
 | 
						|
                       &Map
 | 
						|
                       );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
    PrdBuffer   = (VOID *) ((UINTN) DeviceAddress);
 | 
						|
    TempPrdAddr = UsedPrdAddr;
 | 
						|
    while (TRUE) {
 | 
						|
 | 
						|
      ByteAvailable = 0x10000 - ((UINTN) PrdBuffer & 0xFFFF);
 | 
						|
 | 
						|
      if (ByteCount <= ByteAvailable) {
 | 
						|
        TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
 | 
						|
        TempPrdAddr->ByteCount      = (UINT16) ByteCount;
 | 
						|
        TempPrdAddr->EndOfTable     = 0x8000;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      TempPrdAddr->RegionBaseAddr = (UINT32) ((UINTN) PrdBuffer);
 | 
						|
      TempPrdAddr->ByteCount      = (UINT16) ByteAvailable;
 | 
						|
 | 
						|
      ByteCount -= ByteAvailable;
 | 
						|
      PrdBuffer += ByteAvailable;
 | 
						|
      TempPrdAddr++;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Set the base address to BMID register
 | 
						|
    //
 | 
						|
    IdeDev->PciIo->Io.Write (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint32,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmid,
 | 
						|
                        1,
 | 
						|
                        &UsedPrdAddr
 | 
						|
                        );
 | 
						|
 | 
						|
    //
 | 
						|
    // Set BMIC register to identify the operation direction
 | 
						|
    //
 | 
						|
    IdeDev->PciIo->Io.Read (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    if (UdmaOp == AtaUdmaReadExtOp || UdmaOp == AtaUdmaReadOp) {
 | 
						|
      RegisterValue |= BMIC_NREAD;
 | 
						|
    } else {
 | 
						|
      RegisterValue &= ~((UINT8) BMIC_NREAD);
 | 
						|
    }
 | 
						|
 | 
						|
    IdeDev->PciIo->Io.Write (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    if (UdmaOp == AtaUdmaWriteExtOp || UdmaOp == AtaUdmaReadExtOp) {
 | 
						|
      Status = AtaCommandIssueExt (
 | 
						|
                 IdeDev,
 | 
						|
                 AtaCommand,
 | 
						|
                 Device,
 | 
						|
                 0,
 | 
						|
                 (UINT16) NumberOfBlocks,
 | 
						|
                 StartLba
 | 
						|
                 );
 | 
						|
    } else {
 | 
						|
      Status = AtaCommandIssue (
 | 
						|
                 IdeDev,
 | 
						|
                 AtaCommand,
 | 
						|
                 Device,
 | 
						|
                 0,
 | 
						|
                 (UINT16) NumberOfBlocks,
 | 
						|
                 StartLba
 | 
						|
                 );
 | 
						|
    }
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);
 | 
						|
      IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Set START bit of BMIC register
 | 
						|
    //
 | 
						|
    IdeDev->PciIo->Io.Read (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    RegisterValue |= BMIC_START;
 | 
						|
 | 
						|
    IdeDev->PciIo->Io.Write (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    //
 | 
						|
    // Check the INTERRUPT and ERROR bit of BMIS
 | 
						|
    // Max transfer number of sectors for one command is 65536(32Mbyte),
 | 
						|
    // it will cost 1 second to transfer these data in UDMA mode 2(33.3MBps).
 | 
						|
    // So set the variable Count to 2000, for about 2 second timeout time.
 | 
						|
    //
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
    Count = 2000;
 | 
						|
    while (TRUE) {
 | 
						|
 | 
						|
      IdeDev->PciIo->Io.Read (
 | 
						|
                          IdeDev->PciIo,
 | 
						|
                          EfiPciIoWidthUint8,
 | 
						|
                          EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                          IoPortForBmis,
 | 
						|
                          1,
 | 
						|
                          &RegisterValue
 | 
						|
                          );
 | 
						|
      if (((RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) != 0) || (Count == 0)) {
 | 
						|
        if (((RegisterValue & BMIS_ERROR) != 0) || (Count == 0)) {
 | 
						|
		  Status = EFI_DEVICE_ERROR;
 | 
						|
		  break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      gBS->Stall (1000);
 | 
						|
      Count --;
 | 
						|
    }
 | 
						|
 | 
						|
    IdeDev->PciIo->FreeBuffer (IdeDev->PciIo, PageCount, MemPage);
 | 
						|
    IdeDev->PciIo->Unmap (IdeDev->PciIo, Map);
 | 
						|
    //
 | 
						|
    // Read BMIS register and clear ERROR and INTR bit
 | 
						|
    //
 | 
						|
    IdeDev->PciIo->Io.Read (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmis,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    RegisterValue |= (BMIS_INTERRUPT | BMIS_ERROR);
 | 
						|
 | 
						|
    IdeDev->PciIo->Io.Write (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmis,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
	//
 | 
						|
    // Read Status Register of IDE device to clear interrupt
 | 
						|
    //
 | 
						|
    RegisterValue = IDEReadPortB(IdeDev->PciIo,IdeDev->IoPort->Reg.Status);
 | 
						|
    //
 | 
						|
    // Clear START bit of BMIC register
 | 
						|
    //
 | 
						|
    IdeDev->PciIo->Io.Read (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    RegisterValue &= ~((UINT8) BMIC_START);
 | 
						|
 | 
						|
    IdeDev->PciIo->Io.Write (
 | 
						|
                        IdeDev->PciIo,
 | 
						|
                        EfiPciIoWidthUint8,
 | 
						|
                        EFI_PCI_IO_PASS_THROUGH_BAR,
 | 
						|
                        IoPortForBmic,
 | 
						|
                        1,
 | 
						|
                        &RegisterValue
 | 
						|
                        );
 | 
						|
 | 
						|
    if ((RegisterValue & BMIS_ERROR) != 0) {
 | 
						|
      return EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
	if (EFI_ERROR (Status)) {
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
 | 
						|
    StartLba += NumberOfBlocks;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Disable interrupt of Select device
 | 
						|
  //
 | 
						|
  IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl);
 | 
						|
  DeviceControl |= ATA_CTLREG_IEN_L;
 | 
						|
  IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 |