mirror of https://github.com/acidanthera/audk.git
3691 lines
104 KiB
C
3691 lines
104 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2006, Intel Corporation
|
||
|
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.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
ata.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Revision History
|
||
|
|
||
|
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"
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaReadSectorsExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN OUT VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
);
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaWriteSectorsExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
);
|
||
|
|
||
|
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
|
||
|
);
|
||
|
|
||
|
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
|
||
|
);
|
||
|
|
||
|
EFI_STATUS
|
||
|
ATAIdentify (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
ATAIdentify
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
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.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure,used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
Identify ATA device successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
ATA Identify Device Command failed or device is not
|
||
|
ATA device.
|
||
|
|
||
|
|
||
|
Notes:
|
||
|
parameter IdeDev will be updated in this function.
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_IDENTIFY_DATA *AtaIdentifyPointer;
|
||
|
UINT32 Capacity;
|
||
|
UINT8 DeviceSelect;
|
||
|
|
||
|
//
|
||
|
// AtaIdentifyPointer is used for accommodating returned IDENTIFY data of
|
||
|
// the ATA Identify command
|
||
|
//
|
||
|
AtaIdentifyPointer = (EFI_IDENTIFY_DATA *) AllocateZeroPool (sizeof (EFI_IDENTIFY_DATA));
|
||
|
|
||
|
//
|
||
|
// use ATA PIO Data In protocol to send ATA Identify command
|
||
|
// and receive data from device
|
||
|
//
|
||
|
DeviceSelect = 0;
|
||
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
|
||
|
Status = AtaPioDataIn (
|
||
|
IdeDev,
|
||
|
(VOID *) AtaIdentifyPointer,
|
||
|
sizeof (EFI_IDENTIFY_DATA),
|
||
|
IDENTIFY_DRIVE_CMD,
|
||
|
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;
|
||
|
}
|
||
|
//
|
||
|
// 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;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (AtaIdentifyPointer);
|
||
|
//
|
||
|
// Make sure the pIdData will not be freed again.
|
||
|
//
|
||
|
IdeDev->pIdData = NULL;
|
||
|
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaAtapi6Identify (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaAtapi6Identify
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
This function is called by ATAIdentify() to identity whether this disk
|
||
|
supports ATA/ATAPI6 48bit addressing, ie support >120G capacity
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
EFI_SUCCESS - The disk specified by IdeDev is a Atapi6 supported one
|
||
|
and 48-bit addressing must be used
|
||
|
|
||
|
EFI_UNSUPPORTED - The disk dosn't not support Atapi6 or it supports but
|
||
|
the capacity is below 120G, 48bit addressing is not
|
||
|
needed
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
This function must be called after DEVICE_IDENTITY command has been
|
||
|
successfully returned
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
UINT8 Index;
|
||
|
EFI_LBA TmpLba;
|
||
|
EFI_LBA Capacity;
|
||
|
EFI_IDENTIFY_DATA *Atapi6IdentifyStruct;
|
||
|
|
||
|
if (IdeDev->pIdData == NULL) {
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
Atapi6IdentifyStruct = IdeDev->pIdData;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PrintAtaModuleName (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
PrintAtaModuleName
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is called by ATAIdentify() or ATAPIIdentify()
|
||
|
to print device's module name.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
Returns:
|
||
|
no returns.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
if (IdeDev->pIdData == NULL) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
SwapStringChars (IdeDev->ModelName, IdeDev->pIdData->AtaData.ModelName, 40);
|
||
|
IdeDev->ModelName[40] = 0x00;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaPioDataIn
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is used to send out ATA commands conforms to the
|
||
|
PIO Data In Protocol.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *Buffer
|
||
|
buffer contained data transferred from device to host.
|
||
|
|
||
|
UINT32 IN ByteCount
|
||
|
data size in byte unit of the buffer.
|
||
|
|
||
|
UINT8 IN AtaCommand
|
||
|
value of the Command Register
|
||
|
|
||
|
UINT8 IN Head
|
||
|
value of the Head/Device Register
|
||
|
|
||
|
UINT8 IN SectorCount
|
||
|
value of the Sector Count Register
|
||
|
|
||
|
UINT8 IN SectorNumber
|
||
|
value of the Sector Number Register
|
||
|
|
||
|
UINT8 IN CylinderLsb
|
||
|
value of the low byte of the Cylinder Register
|
||
|
|
||
|
UINT8 IN CylinderMsb
|
||
|
value of the high byte of the Cylinder Register
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
send out the ATA command and device send required
|
||
|
data successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
command sent failed.
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: ByteCount - add argument and description to function comment
|
||
|
// TODO: AtaCommand - add argument and description to function comment
|
||
|
// TODO: Head - add argument and description to function comment
|
||
|
// TODO: SectorCount - add argument and description to function comment
|
||
|
// TODO: SectorNumber - add argument and description to function comment
|
||
|
// TODO: CylinderLsb - add argument and description to function comment
|
||
|
// TODO: CylinderMsb - add argument and description to function comment
|
||
|
{
|
||
|
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 == SET_FEATURES_CMD) {
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaPioDataOut
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is used to send out ATA commands conforms to the
|
||
|
PIO Data Out Protocol.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *Buffer
|
||
|
buffer contained data transferred from host to device.
|
||
|
|
||
|
UINT32 IN ByteCount
|
||
|
data size in byte unit of the buffer.
|
||
|
|
||
|
UINT8 IN AtaCommand
|
||
|
value of the Command Register
|
||
|
|
||
|
UINT8 IN Head
|
||
|
value of the Head/Device Register
|
||
|
|
||
|
UINT8 IN SectorCount
|
||
|
value of the Sector Count Register
|
||
|
|
||
|
UINT8 IN SectorNumber
|
||
|
value of the Sector Number Register
|
||
|
|
||
|
UINT8 IN CylinderLsb
|
||
|
value of the low byte of the Cylinder Register
|
||
|
|
||
|
UINT8 IN CylinderMsb
|
||
|
value of the high byte of the Cylinder Register
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
send out the ATA command and device received required
|
||
|
data successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
command sent failed.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: ByteCount - add argument and description to function comment
|
||
|
// TODO: AtaCommand - add argument and description to function comment
|
||
|
// TODO: Head - add argument and description to function comment
|
||
|
// TODO: SectorCount - add argument and description to function comment
|
||
|
// TODO: SectorNumber - add argument and description to function comment
|
||
|
// TODO: CylinderLsb - add argument and description to function comment
|
||
|
// TODO: CylinderMsb - add argument and description to function comment
|
||
|
{
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
CheckErrorStatus (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
CheckErrorStatus
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
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.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
No err information in the Status Register.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
Any err information in the Status Register.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
UINT8 StatusRegister;
|
||
|
|
||
|
//#ifdef EFI_DEBUG
|
||
|
|
||
|
UINT8 ErrorRegister;
|
||
|
|
||
|
//#endif
|
||
|
|
||
|
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
||
|
|
||
|
DEBUG_CODE (
|
||
|
|
||
|
if (StatusRegister & DWF) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Write Fault\n",
|
||
|
StatusRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (StatusRegister & CORR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Corrected Data\n",
|
||
|
StatusRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (StatusRegister & ERR) {
|
||
|
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
||
|
|
||
|
if (ErrorRegister & BBK_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Bad Block Detected\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ErrorRegister & UNC_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ErrorRegister & MC_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Media Change\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ErrorRegister & ABRT_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Abort\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ErrorRegister & TK0NF_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ErrorRegister & AMNF_ERR) {
|
||
|
DEBUG (
|
||
|
(EFI_D_BLKIO,
|
||
|
"CheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",
|
||
|
ErrorRegister)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
);
|
||
|
|
||
|
if ((StatusRegister & (ERR | DWF | CORR)) == 0) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaReadSectors (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA Lba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaReadSectors
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is called by the AtaBlkIoReadBlocks() to perform
|
||
|
reading from media in block unit.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the destination buffer for the data.
|
||
|
|
||
|
EFI_LBA IN Lba
|
||
|
The starting logical block address to read from
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
return status is fully dependent on the return status
|
||
|
of AtaPioDataIn() function.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: Lba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
{
|
||
|
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 = READ_SECTORS_CMD;
|
||
|
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaWriteSectors (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *BufferData,
|
||
|
IN EFI_LBA Lba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaWriteSectors
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is called by the AtaBlkIoWriteBlocks() to perform
|
||
|
writing onto media in block unit.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure,used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *BufferData
|
||
|
A pointer to the source buffer for the data.
|
||
|
|
||
|
EFI_LBA IN Lba
|
||
|
The starting logical block address to write onto
|
||
|
the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
return status is fully dependent on the return status
|
||
|
of AtaPioDataOut() function.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: BufferData - add argument and description to function comment
|
||
|
// TODO: Lba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
{
|
||
|
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 = WRITE_SECTORS_CMD;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaSoftReset (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaSoftReset
|
||
|
|
||
|
Purpose:
|
||
|
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.
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
Soft reset completes successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
Any step during the reset process is failed.
|
||
|
Notes:
|
||
|
The registers initial values after ATA soft reset are different
|
||
|
to the ATA device and ATAPI device.
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
|
||
|
UINT8 DeviceControl;
|
||
|
|
||
|
DeviceControl = 0;
|
||
|
//
|
||
|
// set SRST bit to initiate soft reset
|
||
|
//
|
||
|
DeviceControl |= SRST;
|
||
|
|
||
|
//
|
||
|
// disable Interrupt
|
||
|
//
|
||
|
DeviceControl |= bit1;
|
||
|
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
gBS->Stall (10);
|
||
|
|
||
|
//
|
||
|
// Enable interrupt to support UDMA, and clear SRST bit
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
//
|
||
|
// slave device needs at most 31s to clear BSY
|
||
|
//
|
||
|
if (WaitForBSYClear (IdeDev, 31000) == EFI_TIMEOUT) {
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaBlkIoReadBlocks (
|
||
|
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
|
||
|
IN UINT32 MediaId,
|
||
|
IN EFI_LBA LBA,
|
||
|
IN UINTN BufferSize,
|
||
|
OUT VOID *Buffer
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaBlkIoReadBlocks
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is the ATA implementation for ReadBlocks in the
|
||
|
Block I/O Protocol interface.
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeBlkIoDevice
|
||
|
Indicates the calling context.
|
||
|
|
||
|
UINT32 IN MediaId
|
||
|
The media id that the read request is for.
|
||
|
|
||
|
EFI_LBA IN LBA
|
||
|
The starting logical block address to read from
|
||
|
on the device.
|
||
|
|
||
|
UINTN IN BufferSize
|
||
|
The size of the Buffer in bytes. This must be a
|
||
|
multiple of the intrinsic block size of the device.
|
||
|
|
||
|
VOID 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.
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
Read Blocks successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
Read Blocks failed.
|
||
|
|
||
|
EFI_NO_MEDIA
|
||
|
There is no media in the device.
|
||
|
|
||
|
EFI_MEDIA_CHANGE
|
||
|
The MediaId is not for the current media.
|
||
|
|
||
|
EFI_BAD_BUFFER_SIZE
|
||
|
The BufferSize parameter is not a multiple of the
|
||
|
intrinsic block size of the device.
|
||
|
|
||
|
EFI_INVALID_PARAMETER
|
||
|
The read request contains LBAs that are not valid,
|
||
|
or the data buffer is not valid.
|
||
|
|
||
|
Notes:
|
||
|
If Read Block error because of device error, this function will call
|
||
|
AtaSoftReset() function to reset device.
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeBlkIoDevice - add argument and description to function comment
|
||
|
// TODO: MediaId - add argument and description to function comment
|
||
|
// TODO: LBA - add argument and description to function comment
|
||
|
// TODO: BufferSize - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: EFI_MEDIA_CHANGED - add return value to function comment
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
|
||
|
//
|
||
|
// For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 read block mechanism
|
||
|
//
|
||
|
Status = AtaUdmaReadExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = AtaReadSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// For ATA-3 compatible device, use ATA-3 read block mechanism
|
||
|
// Notice DMA operation can only handle 32bit address
|
||
|
//
|
||
|
if ((UINTN) Buffer <= 0xFFFFFFFF) {
|
||
|
Status = AtaUdmaRead (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
}
|
||
|
|
||
|
if (EFI_ERROR (Status) || ((UINTN) Buffer > 0xFFFFFFFF)) {
|
||
|
Status = AtaReadSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
AtaSoftReset (IdeBlkIoDevice);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaBlkIoWriteBlocks (
|
||
|
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
|
||
|
IN UINT32 MediaId,
|
||
|
IN EFI_LBA LBA,
|
||
|
IN UINTN BufferSize,
|
||
|
OUT VOID *Buffer
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaBlkIoWriteBlocks
|
||
|
|
||
|
|
||
|
Purpose:
|
||
|
This function is the ATA implementation for WriteBlocks in the
|
||
|
Block I/O Protocol interface.
|
||
|
|
||
|
Parameters:
|
||
|
IDE_BLK_IO_DEV IN *IdeBlkIoDevice
|
||
|
Indicates the calling context.
|
||
|
|
||
|
UINT32 IN MediaId
|
||
|
The media id that the write request is for.
|
||
|
|
||
|
EFI_LBA IN LBA
|
||
|
The starting logical block address to write onto
|
||
|
the device.
|
||
|
|
||
|
UINTN IN BufferSize
|
||
|
The size of the Buffer in bytes. This must be a
|
||
|
multiple of the intrinsic block size of the device.
|
||
|
|
||
|
VOID 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.
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
EFI_SUCCESS
|
||
|
Write Blocks successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
Write Blocks failed.
|
||
|
|
||
|
EFI_NO_MEDIA
|
||
|
There is no media in the device.
|
||
|
|
||
|
EFI_MEDIA_CHANGE
|
||
|
The MediaId is not for the current media.
|
||
|
|
||
|
EFI_BAD_BUFFER_SIZE
|
||
|
The BufferSize parameter is not a multiple of the
|
||
|
intrinsic block size of the device.
|
||
|
|
||
|
EFI_INVALID_PARAMETER
|
||
|
The write request contains LBAs that are not valid,
|
||
|
or the data buffer is not valid.
|
||
|
|
||
|
Notes:
|
||
|
If Write Block error because of device error, this function will call
|
||
|
AtaSoftReset() function to reset device.
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeBlkIoDevice - add argument and description to function comment
|
||
|
// TODO: MediaId - add argument and description to function comment
|
||
|
// TODO: LBA - add argument and description to function comment
|
||
|
// TODO: BufferSize - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: EFI_MEDIA_CHANGED - add return value to function comment
|
||
|
{
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if (IdeBlkIoDevice->Type == Ide48bitAddressingHardDisk) {
|
||
|
//
|
||
|
// For ATA/ATAPI-6 device(capcity > 120GB), use ATA-6 write block mechanism
|
||
|
//
|
||
|
Status = AtaUdmaWriteExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
Status = AtaWriteSectorsExt (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// For ATA-3 compatible device, use ATA-3 write block mechanism
|
||
|
//
|
||
|
Status = AtaUdmaWrite (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
if (EFI_ERROR (Status) || ((UINTN) Buffer > 0xFFFFFFFF)) {
|
||
|
Status = AtaWriteSectors (IdeBlkIoDevice, Buffer, LBA, NumberOfBlocks);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
AtaSoftReset (IdeBlkIoDevice);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaReadSectorsExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaReadSectorsExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the destination buffer for the data.
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
The starting logical block address to read from
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
return status is fully dependent on the return status
|
||
|
of AtaPioDataInExt() function.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
{
|
||
|
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 = READ_SECTORS_EXT_CMD;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaWriteSectorsExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaWriteSectorsExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure,used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the source buffer for the data.
|
||
|
|
||
|
EFI_LBA IN Lba
|
||
|
The starting logical block address to write onto
|
||
|
the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
return status is fully dependent on the return status
|
||
|
of AtaPioDataOutExt() function.
|
||
|
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
{
|
||
|
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 = WRITE_SECTORS_EXT_CMD;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaPioDataInExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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:
|
||
|
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!)
|
||
|
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN OUT *Buffer
|
||
|
buffer contained data transferred from device to host.
|
||
|
|
||
|
UINT32 IN ByteCount
|
||
|
data size in byte unit of the buffer.
|
||
|
|
||
|
UINT8 IN AtaCommand
|
||
|
value of the Command Register
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
the start LBA of this transaction
|
||
|
|
||
|
UINT16 IN SectorCount
|
||
|
the count of sectors to be transfered
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
EFI_SUCCESS
|
||
|
send out the ATA command and device send required
|
||
|
data successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
command sent failed.
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: ByteCount - add argument and description to function comment
|
||
|
// TODO: AtaCommand - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: SectorCount - add argument and description to function comment
|
||
|
{
|
||
|
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 == SET_FEATURES_CMD) {
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaPioDataOutExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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:
|
||
|
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!)
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *Buffer
|
||
|
buffer contained data transferred from host to device.
|
||
|
|
||
|
UINT32 IN ByteCount
|
||
|
data size in byte unit of the buffer.
|
||
|
|
||
|
UINT8 IN AtaCommand
|
||
|
value of the Command Register
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
the start LBA of this transaction
|
||
|
|
||
|
UINT16 IN SectorCount
|
||
|
the count of sectors to be transfered
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
EFI_SUCCESS
|
||
|
send out the ATA command and device receive required
|
||
|
data successfully.
|
||
|
|
||
|
EFI_DEVICE_ERROR
|
||
|
command sent failed.
|
||
|
Notes:
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: Buffer - add argument and description to function comment
|
||
|
// TODO: ByteCount - add argument and description to function comment
|
||
|
// TODO: AtaCommand - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: SectorCount - add argument and description to function comment
|
||
|
{
|
||
|
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 == SET_FEATURES_CMD) {
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
AtaSMARTSupport (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
AtaSMARTSupport
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
Enable SMART of the disk if supported
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure,used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
NONE
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
{
|
||
|
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_SMART_CMD,
|
||
|
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));
|
||
|
|
||
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
|
||
|
Status = AtaPioDataIn (
|
||
|
IdeDev,
|
||
|
(VOID *) TmpAtaIdentifyPointer,
|
||
|
sizeof (EFI_IDENTIFY_DATA),
|
||
|
IDENTIFY_DRIVE_CMD,
|
||
|
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_SMART_CMD,
|
||
|
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 ;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Send ATA Ext command into device with NON_DATA protocol
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
IdeDev - Standard IDE device private data structure
|
||
|
AtaCommand - The ATA command to be sent
|
||
|
Device - The value in Device register
|
||
|
Feature - The value in Feature register
|
||
|
SectorCount - The value in SectorCount register
|
||
|
LbaAddress - The LBA address in 48-bit mode
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
EFI_SUCCESS - Reading succeed
|
||
|
EFI_DEVICE_ERROR - Error executing commands on this device
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Send ATA Ext command into device with NON_DATA protocol
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
IdeDev - Standard IDE device private data structure
|
||
|
AtaCommand - The ATA command to be sent
|
||
|
Device - The value in Device register
|
||
|
Feature - The value in Feature register
|
||
|
SectorCount - The value in SectorCount register
|
||
|
LbaAddress - The LBA address in 48-bit mode
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
EFI_SUCCESS - Reading succeed
|
||
|
EFI_DEVICE_ERROR - Error executing commands on this device
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
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 |= 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;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaUdmaReadExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaUdmaReadExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the destination buffer for the data.
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
The starting logical block address to read from
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
The device status of UDMA operation. If the operation is
|
||
|
successful, return EFI_SUCCESS.
|
||
|
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
// 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
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
//
|
||
|
// Channel and device differential. Select device.
|
||
|
//
|
||
|
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
|
||
|
|
||
|
//
|
||
|
// Enable interrupt to support UDMA and Select device
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemainBlockNum = NumberOfBlocks;
|
||
|
while (RemainBlockNum > 0) {
|
||
|
|
||
|
if (RemainBlockNum >= MAX_DMA_EXT_COMMAND_SECTORS) {
|
||
|
//
|
||
|
// SectorCount is used to record the number of sectors to be read
|
||
|
// Max 65536 sectors can be transfered at a time.
|
||
|
//
|
||
|
NumberOfBlocks = MAX_DMA_EXT_COMMAND_SECTORS;
|
||
|
RemainBlockNum -= MAX_DMA_EXT_COMMAND_SECTORS;
|
||
|
} 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
|
||
|
//
|
||
|
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
PrdBuffer = DataBuffer;
|
||
|
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
|
||
|
);
|
||
|
|
||
|
RegisterValue |= BMIC_nREAD;
|
||
|
|
||
|
IdeDev->PciIo->Io.Write (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmic,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Issue READ DMA EXT command
|
||
|
//
|
||
|
Status = AtaCommandIssueExt (
|
||
|
IdeDev,
|
||
|
READ_DMA_EXT_CMD,
|
||
|
Device,
|
||
|
0,
|
||
|
(UINT16) NumberOfBlocks,
|
||
|
StartLba
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
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
|
||
|
//
|
||
|
while (TRUE) {
|
||
|
|
||
|
IdeDev->PciIo->Io.Read (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmis,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
|
||
|
if (RegisterValue & BMIS_ERROR) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gBS->Stall (1000);
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
|
||
|
//
|
||
|
// Set 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) {
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
|
||
|
StartLba += NumberOfBlocks;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaUdmaRead (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaUdmaRead
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the destination buffer for the data.
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
The starting logical block address to read from
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
The device status of UDMA operation. If the operation is
|
||
|
successful, return EFI_SUCCESS.
|
||
|
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
// 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
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
//
|
||
|
// Channel and device differential
|
||
|
//
|
||
|
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
|
||
|
|
||
|
//
|
||
|
// Enable interrupt to support UDMA and Select device
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemainBlockNum = NumberOfBlocks;
|
||
|
while (RemainBlockNum > 0) {
|
||
|
|
||
|
if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {
|
||
|
//
|
||
|
// SectorCount is used to record the number of sectors to be read
|
||
|
// Max 256 sectors can be transfered at a time.
|
||
|
//
|
||
|
NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;
|
||
|
RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;
|
||
|
} 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
|
||
|
//
|
||
|
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
PrdBuffer = DataBuffer;
|
||
|
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
|
||
|
);
|
||
|
|
||
|
RegisterValue |= BMIC_nREAD;
|
||
|
|
||
|
IdeDev->PciIo->Io.Write (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmic,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Issue READ DMA command
|
||
|
//
|
||
|
Status = AtaCommandIssue (
|
||
|
IdeDev,
|
||
|
READ_DMA_CMD,
|
||
|
Device,
|
||
|
0,
|
||
|
(UINT16) NumberOfBlocks,
|
||
|
StartLba
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
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
|
||
|
//
|
||
|
while (TRUE) {
|
||
|
|
||
|
IdeDev->PciIo->Io.Read (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmis,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
|
||
|
if (RegisterValue & BMIS_ERROR) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gBS->Stall (1000);
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
|
||
|
//
|
||
|
// Set 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) {
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
|
||
|
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
|
||
|
StartLba += NumberOfBlocks;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaUdmaWriteExt (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaUdmaWriteExt
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the source buffer for the data.
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
The starting logical block address to write to
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
The device status of UDMA operation. If the operation is
|
||
|
successful, return EFI_SUCCESS.
|
||
|
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
// 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
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
//
|
||
|
// Channel and device differential
|
||
|
//
|
||
|
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
|
||
|
|
||
|
//
|
||
|
// Enable interrupt to support UDMA and Select device
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemainBlockNum = NumberOfBlocks;
|
||
|
while (RemainBlockNum > 0) {
|
||
|
|
||
|
if (RemainBlockNum >= MAX_DMA_EXT_COMMAND_SECTORS) {
|
||
|
//
|
||
|
// SectorCount is used to record the number of sectors to be read
|
||
|
// Max 65536 sectors can be transfered at a time.
|
||
|
//
|
||
|
NumberOfBlocks = MAX_DMA_EXT_COMMAND_SECTORS;
|
||
|
RemainBlockNum -= MAX_DMA_EXT_COMMAND_SECTORS;
|
||
|
} 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
|
||
|
//
|
||
|
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
PrdBuffer = DataBuffer;
|
||
|
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
|
||
|
);
|
||
|
//
|
||
|
// 0000 1000
|
||
|
//
|
||
|
RegisterValue &= ~((UINT8) BMIC_nREAD);
|
||
|
|
||
|
IdeDev->PciIo->Io.Write (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmic,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Issue WRITE DMA EXT command
|
||
|
//
|
||
|
Status = AtaCommandIssueExt (
|
||
|
IdeDev,
|
||
|
WRITE_DMA_EXT_CMD,
|
||
|
Device,
|
||
|
0,
|
||
|
(UINT16) NumberOfBlocks,
|
||
|
StartLba
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
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
|
||
|
//
|
||
|
while (TRUE) {
|
||
|
|
||
|
IdeDev->PciIo->Io.Read (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmis,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
|
||
|
if (RegisterValue & BMIS_ERROR) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gBS->Stall (1000);
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
|
||
|
//
|
||
|
// Set 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
|
||
|
);
|
||
|
|
||
|
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
|
||
|
StartLba += NumberOfBlocks;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
AtaUdmaWrite (
|
||
|
IN IDE_BLK_IO_DEV *IdeDev,
|
||
|
IN VOID *DataBuffer,
|
||
|
IN EFI_LBA StartLba,
|
||
|
IN UINTN NumberOfBlocks
|
||
|
)
|
||
|
/*++
|
||
|
Name:
|
||
|
|
||
|
AtaUdmaWrite
|
||
|
|
||
|
Purpose:
|
||
|
|
||
|
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
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
IDE_BLK_IO_DEV IN *IdeDev
|
||
|
pointer pointing to IDE_BLK_IO_DEV data structure, used
|
||
|
to record all the information of the IDE device.
|
||
|
|
||
|
VOID IN *DataBuffer
|
||
|
A pointer to the source buffer for the data.
|
||
|
|
||
|
EFI_LBA IN StartLba
|
||
|
The starting logical block address to write to
|
||
|
on the device media.
|
||
|
|
||
|
UINTN IN NumberOfBlocks
|
||
|
The number of transfer data blocks.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
The device status of UDMA operation. If the operation is
|
||
|
successful, return EFI_SUCCESS.
|
||
|
|
||
|
--*/
|
||
|
// TODO: function comment is missing 'Routine Description:'
|
||
|
// TODO: function comment is missing 'Arguments:'
|
||
|
// TODO: IdeDev - add argument and description to function comment
|
||
|
// TODO: DataBuffer - add argument and description to function comment
|
||
|
// TODO: StartLba - add argument and description to function comment
|
||
|
// TODO: NumberOfBlocks - add argument and description to function comment
|
||
|
// 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
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
//
|
||
|
// Channel and device differential
|
||
|
//
|
||
|
Device = (UINT8) ((IdeDev->Device << 4) | 0xe0);
|
||
|
|
||
|
//
|
||
|
// Enable interrupt to support UDMA
|
||
|
//
|
||
|
DeviceControl = 0;
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, DeviceControl);
|
||
|
|
||
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, Device);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RemainBlockNum = NumberOfBlocks;
|
||
|
while (RemainBlockNum > 0) {
|
||
|
|
||
|
if (RemainBlockNum >= MAX_DMA_COMMAND_SECTORS) {
|
||
|
//
|
||
|
// SectorCount is used to record the number of sectors to be read
|
||
|
// Max 256 sectors can be transfered at a time.
|
||
|
//
|
||
|
NumberOfBlocks = MAX_DMA_COMMAND_SECTORS;
|
||
|
RemainBlockNum -= MAX_DMA_COMMAND_SECTORS;
|
||
|
} 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
|
||
|
//
|
||
|
PrdAddr = (IDE_DMA_PRD *) AllocateZeroPool ((2 * PrdTableNum * sizeof (IDE_DMA_PRD)));
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
PrdBuffer = DataBuffer;
|
||
|
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
|
||
|
);
|
||
|
//
|
||
|
// 0000 1000
|
||
|
//
|
||
|
RegisterValue &= ~((UINT8) BMIC_nREAD);
|
||
|
|
||
|
IdeDev->PciIo->Io.Write (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmic,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Issue WRITE DMA command
|
||
|
//
|
||
|
Status = AtaCommandIssue (
|
||
|
IdeDev,
|
||
|
WRITE_DMA_CMD,
|
||
|
Device,
|
||
|
0,
|
||
|
(UINT16) NumberOfBlocks,
|
||
|
StartLba
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
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
|
||
|
//
|
||
|
while (TRUE) {
|
||
|
|
||
|
IdeDev->PciIo->Io.Read (
|
||
|
IdeDev->PciIo,
|
||
|
EfiPciIoWidthUint8,
|
||
|
EFI_PCI_IO_PASS_THROUGH_BAR,
|
||
|
IoPortForBmis,
|
||
|
1,
|
||
|
&RegisterValue
|
||
|
);
|
||
|
if (RegisterValue & (BMIS_INTERRUPT | BMIS_ERROR)) {
|
||
|
if (RegisterValue & BMIS_ERROR) {
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
return EFI_DEVICE_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
gBS->Stall (1000);
|
||
|
}
|
||
|
|
||
|
gBS->FreePool (PrdAddr);
|
||
|
|
||
|
//
|
||
|
// Set 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
|
||
|
);
|
||
|
|
||
|
DataBuffer = (UINT8 *) DataBuffer + NumberOfBlocks * IdeDev->BlkIo.Media->BlockSize;
|
||
|
StartLba += NumberOfBlocks;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|