[FREELDR] Fix IDE driver failures on real hardware. (#2255)

- Changes in device detection code:
    * Properly check device signature.
    * Сorrectly reset the IDE controller.
    * Remove check for device type code.
- Fix CD-ROM sector read issue:
    * Changed ATAPI packet opcode to be more universal (Some devices may not have READ12 support).
    * Always use 2048 bytes as sector size when reading the data from a disc.
    * Modify WaitForFlags() to stop polling if the error bit was set.
    * Changed timeout to 31 seconds (ATA default value).
- Add more informational messages in DEBUG mode.
- Fix invalid IDE register value.
- Fix registers names.

Tested by Stanislav Motylkov and Daniel Reimer on XQEMU and on real
hardware MS Xbox revision 1.3 with Philips DVD drive.
It has also been tested manually on PC with a SONY DVD drive.

CORE-16628 CORE-16216
This commit is contained in:
Dmitry Borisov 2020-01-17 19:39:28 +06:00 committed by Hermès Bélusca-Maïto
parent dc81ecf84b
commit a55bab8d1d
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 231 additions and 114 deletions

View file

@ -1,8 +1,8 @@
/* /*
* PROJECT: FreeLoader * PROJECT: FreeLoader
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: ATA/ATAPI polled I/O driver. * PURPOSE: ATA/ATAPI programmed I/O driver.
* COPYRIGHT: Copyright 2019 Dmitry Borisov (di.sean@protonmail.com) * COPYRIGHT: Copyright 2019-2020 Dmitry Borisov (di.sean@protonmail.com)
*/ */
/* INCLUDES *******************************************************************/ /* INCLUDES *******************************************************************/
@ -21,10 +21,7 @@ DBG_DEFAULT_CHANNEL(DISK);
#define TAG_ATA_DEVICE 'DatA' #define TAG_ATA_DEVICE 'DatA'
#define ATAPI_PACKET_SIZE(IdentifyData) (IdentifyData.AtapiCmdSize ? 16 : 12) #define ATAPI_PACKET_SIZE(IdentifyData) (IdentifyData.AtapiCmdSize ? 16 : 12)
#define ATA_STATUS_TIMEOUT 31e5
/* Used in WaitForFlags() and should be 31 seconds (31e5), but it's too much for polled I/O */
#define ATA_STATUS_TIMEOUT 36000
#define ATA_READ_TIMEOUT 4e5
#define AtaWritePort(Channel, Port, Data) \ #define AtaWritePort(Channel, Port, Data) \
WRITE_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port)), (Data)) WRITE_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port)), (Data))
@ -70,6 +67,22 @@ WaitForFlags(
static static
BOOLEAN BOOLEAN
WaitForFlagsOr(
IN UCHAR Channel,
IN UCHAR FirstValue,
IN UCHAR SecondValue,
IN ULONG Timeout
);
static
BOOLEAN
WaitForBusy(
IN UCHAR Channel,
IN ULONG Timeout
);
static
VOID
SelectDevice( SelectDevice(
IN UCHAR Channel, IN UCHAR Channel,
IN UCHAR DeviceNumber IN UCHAR DeviceNumber
@ -83,6 +96,19 @@ IdentifyDevice(
OUT PDEVICE_UNIT *DeviceUnit OUT PDEVICE_UNIT *DeviceUnit
); );
static
BOOLEAN
AtapiRequestSense(
IN PDEVICE_UNIT DeviceUnit,
OUT PSENSE_DATA SenseData
);
static
VOID
AtapiPrintSenseData(
IN PDEVICE_UNIT DeviceUnit
);
static static
BOOLEAN BOOLEAN
AtapiReadyCheck( AtapiReadyCheck(
@ -274,21 +300,23 @@ AtaReadLogicalSectorsLBA(
} }
/* Select the drive */ /* Select the drive */
if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber);
if (!WaitForBusy(DeviceUnit->Channel, ATA_STATUS_TIMEOUT))
{
ERR("AtaReadLogicalSectorsLBA() failed. Device is busy.\n");
return FALSE; return FALSE;
}
/* Disable interrupts */ /* Disable interrupts */
#ifndef SARCH_PC98 AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_Control, IDE_DC_DISABLE_INTERRUPTS);
AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS);
StallExecutionProcessor(1); StallExecutionProcessor(1);
#endif
if (UseLBA48) if (UseLBA48)
{ {
/* FIFO */ /* FIFO */
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, 0); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, 0);
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, ATA_PIO); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, ATA_PIO);
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, (BlockCount & 0xFF) >> 8); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, (BlockCount >> 8) & 0xFF);
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, BlockCount & 0xFF); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, BlockCount & 0xFF);
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, (Lba >> 24) & 0xFF); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, (Lba >> 24) & 0xFF);
AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, Lba & 0xFF); AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, Lba & 0xFF);
@ -321,8 +349,8 @@ AtaReadLogicalSectorsLBA(
for (RemainingBlockCount = BlockCount; RemainingBlockCount > 0; --RemainingBlockCount) for (RemainingBlockCount = BlockCount; RemainingBlockCount > 0; --RemainingBlockCount)
{ {
/* Wait for ready to transfer data block */ /* Wait for ready to transfer data block */
if (!WaitForFlags(DeviceUnit->Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR), if (!WaitForFlags(DeviceUnit->Channel, IDE_STATUS_DRQ,
IDE_STATUS_DRQ, ATA_READ_TIMEOUT)) IDE_STATUS_DRQ, ATA_STATUS_TIMEOUT))
{ {
ERR("AtaReadLogicalSectorsLBA() failed. Status: 0x%02x, Error: 0x%02x\n", ERR("AtaReadLogicalSectorsLBA() failed. Status: 0x%02x, Error: 0x%02x\n",
AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Status), AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Status),
@ -351,9 +379,15 @@ AtaSendAtapiPacket(
IN UCHAR PacketSize, IN UCHAR PacketSize,
IN USHORT ByteCount) IN USHORT ByteCount)
{ {
/* No DRQ for TEST UNIT READY */ /*
BOOLEAN NoData = (AtapiPacket[0] == SCSIOP_TEST_UNIT_READY); * REQUEST SENSE is used by driver to clear the ATAPI 'Bus reset' indication.
UCHAR ExpectedFlags = NoData ? IDE_STATUS_DRDY : (IDE_STATUS_DRQ | IDE_STATUS_DRDY); * TEST UNIT READY doesn't require space for returned data.
*/
UCHAR ExpectedFlagsMask = (AtapiPacket[0] == SCSIOP_REQUEST_SENSE) ?
IDE_STATUS_DRDY : (IDE_STATUS_DRQ | IDE_STATUS_DRDY);
UCHAR ExpectedFlags = ((AtapiPacket[0] == SCSIOP_TEST_UNIT_READY) ||
(AtapiPacket[0] == SCSIOP_REQUEST_SENSE)) ?
IDE_STATUS_DRDY : (IDE_STATUS_DRQ | IDE_STATUS_DRDY);
/* PIO mode */ /* PIO mode */
AtaWritePort(Channel, IDX_ATAPI_IO1_o_Feature, ATA_PIO); AtaWritePort(Channel, IDX_ATAPI_IO1_o_Feature, ATA_PIO);
@ -365,8 +399,7 @@ AtaSendAtapiPacket(
/* Prepare to transfer a device command via a command packet */ /* Prepare to transfer a device command via a command packet */
AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET); AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET);
StallExecutionProcessor(50); StallExecutionProcessor(50);
if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR), if (!WaitForFlagsOr(Channel, IDE_STATUS_DRQ, IDE_STATUS_DRDY, ATA_STATUS_TIMEOUT))
IDE_STATUS_DRQ, NoData ? ATA_STATUS_TIMEOUT : ATA_READ_TIMEOUT))
{ {
ERR("AtaSendAtapiPacket(0x%x) failed. A device error occurred Status: 0x%02x, Error: 0x%02x\n", ERR("AtaSendAtapiPacket(0x%x) failed. A device error occurred Status: 0x%02x, Error: 0x%02x\n",
AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error)); AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error));
@ -375,8 +408,7 @@ AtaSendAtapiPacket(
/* Command packet transfer */ /* Command packet transfer */
AtaWriteBuffer(Channel, AtapiPacket, PacketSize); AtaWriteBuffer(Channel, AtapiPacket, PacketSize);
if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_DRDY | IDE_STATUS_ERROR), if (!WaitForFlags(Channel, ExpectedFlagsMask, ExpectedFlags, ATA_STATUS_TIMEOUT))
ExpectedFlags, NoData ? ATA_STATUS_TIMEOUT : ATA_READ_TIMEOUT))
{ {
TRACE("AtaSendAtapiPacket(0x%x) failed. An execution error occurred Status: 0x%02x, Error: 0x%02x\n", TRACE("AtaSendAtapiPacket(0x%x) failed. An execution error occurred Status: 0x%02x, Error: 0x%02x\n",
AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error)); AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error));
@ -398,28 +430,25 @@ AtapiReadLogicalSectorLBA(
BOOLEAN Success; BOOLEAN Success;
/* Select the drive */ /* Select the drive */
if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber);
if (!WaitForBusy(DeviceUnit->Channel, ATA_STATUS_TIMEOUT))
{
ERR("AtapiReadLogicalSectorLBA() failed. Device is busy!\n");
return FALSE; return FALSE;
}
/* Disable interrupts */ /* Disable interrupts */
AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS); AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_Control, IDE_DC_DISABLE_INTERRUPTS);
StallExecutionProcessor(1); StallExecutionProcessor(1);
/* Send the SCSI READ command */ /* Send the SCSI READ command */
RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket)); RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
#if defined(SARCH_PC98)
AtapiPacket[0] = SCSIOP_READ; AtapiPacket[0] = SCSIOP_READ;
AtapiPacket[8] = 1;
AtapiPacket[9] = 0;
#else
AtapiPacket[0] = SCSIOP_READ12;
AtapiPacket[8] = 0;
AtapiPacket[9] = 1;
#endif
AtapiPacket[2] = (SectorNumber >> 24) & 0xFF; AtapiPacket[2] = (SectorNumber >> 24) & 0xFF;
AtapiPacket[3] = (SectorNumber >> 16) & 0xFF; AtapiPacket[3] = (SectorNumber >> 16) & 0xFF;
AtapiPacket[4] = (SectorNumber >> 8) & 0xFF; AtapiPacket[4] = (SectorNumber >> 8) & 0xFF;
AtapiPacket[5] = SectorNumber & 0xFF; AtapiPacket[5] = SectorNumber & 0xFF;
AtapiPacket[8] = 1;
Success = AtaSendAtapiPacket(DeviceUnit->Channel, Success = AtaSendAtapiPacket(DeviceUnit->Channel,
AtapiPacket, AtapiPacket,
ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
@ -427,6 +456,7 @@ AtapiReadLogicalSectorLBA(
if (!Success) if (!Success)
{ {
ERR("AtapiReadLogicalSectorLBA() failed. A read error occurred.\n"); ERR("AtapiReadLogicalSectorLBA() failed. A read error occurred.\n");
AtapiPrintSenseData(DeviceUnit);
return FALSE; return FALSE;
} }
@ -461,11 +491,61 @@ AtapiCapacityDetect(
*SectorSize = (AtapiCapacity[4] << 24) | (AtapiCapacity[5] << 16) | *SectorSize = (AtapiCapacity[4] << 24) | (AtapiCapacity[5] << 16) |
(AtapiCapacity[6] << 8) | AtapiCapacity[7]; (AtapiCapacity[6] << 8) | AtapiCapacity[7];
/* If device reports a non-zero block length, reset to defaults (we use READ command instead of READ CD) */
if (*SectorSize != 0)
*SectorSize = 2048;
} }
else else
{ {
*TotalSectors = 0; *TotalSectors = 0;
*SectorSize = 0; *SectorSize = 0;
AtapiPrintSenseData(DeviceUnit);
}
}
static
BOOLEAN
AtapiRequestSense(
IN PDEVICE_UNIT DeviceUnit,
OUT PSENSE_DATA SenseData)
{
UCHAR AtapiPacket[16];
BOOLEAN Success;
RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
RtlZeroMemory(SenseData, sizeof(SENSE_DATA));
AtapiPacket[0] = SCSIOP_REQUEST_SENSE;
AtapiPacket[4] = SENSE_BUFFER_SIZE;
Success = AtaSendAtapiPacket(DeviceUnit->Channel,
AtapiPacket,
ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
SENSE_BUFFER_SIZE);
if (Success)
{
AtaReadBuffer(DeviceUnit->Channel, SenseData, SENSE_BUFFER_SIZE);
return TRUE;
}
else
{
ERR("Cannot read the sense data.\n");
return FALSE;
}
}
static
VOID
AtapiPrintSenseData(IN PDEVICE_UNIT DeviceUnit)
{
SENSE_DATA SenseData;
if (AtapiRequestSense(DeviceUnit, &SenseData))
{
ERR("SK 0x%x, ASC 0x%x, ASCQ 0x%x\n",
SenseData.SenseKey,
SenseData.AdditionalSenseCode,
SenseData.AdditionalSenseCodeQualifier);
} }
} }
@ -479,7 +559,8 @@ AtapiReadyCheck(IN OUT PDEVICE_UNIT DeviceUnit)
BOOLEAN Success; BOOLEAN Success;
/* Select the drive */ /* Select the drive */
if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber);
if (!WaitForBusy(DeviceUnit->Channel, ATA_STATUS_TIMEOUT))
return FALSE; return FALSE;
/* Send the SCSI TEST UNIT READY command */ /* Send the SCSI TEST UNIT READY command */
@ -490,16 +571,7 @@ AtapiReadyCheck(IN OUT PDEVICE_UNIT DeviceUnit)
ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
0); 0);
/* Send the SCSI REQUEST SENSE command */ if (!AtapiRequestSense(DeviceUnit, &SenseData))
RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
RtlZeroMemory(&SenseData, SENSE_BUFFER_SIZE);
AtapiPacket[0] = SCSIOP_REQUEST_SENSE;
AtapiPacket[4] = SENSE_BUFFER_SIZE;
Success = AtaSendAtapiPacket(DeviceUnit->Channel,
AtapiPacket,
ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
SENSE_BUFFER_SIZE);
if (!Success)
return FALSE; return FALSE;
AtaReadBuffer(DeviceUnit->Channel, &SenseData, SENSE_BUFFER_SIZE); AtaReadBuffer(DeviceUnit->Channel, &SenseData, SENSE_BUFFER_SIZE);
@ -531,7 +603,10 @@ AtapiReadyCheck(IN OUT PDEVICE_UNIT DeviceUnit)
ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
MAXIMUM_CDROM_SIZE); MAXIMUM_CDROM_SIZE);
if (!Success) if (!Success)
{
AtapiPrintSenseData(DeviceUnit);
return FALSE; return FALSE;
}
AtaReadBuffer(DeviceUnit->Channel, &DummyData, MAXIMUM_CDROM_SIZE); AtaReadBuffer(DeviceUnit->Channel, &DummyData, MAXIMUM_CDROM_SIZE);
/* fall through */ /* fall through */
@ -577,15 +652,67 @@ WaitForFlags(
IN UCHAR Flags, IN UCHAR Flags,
IN UCHAR ExpectedValue, IN UCHAR ExpectedValue,
IN ULONG Timeout) IN ULONG Timeout)
{
UCHAR Status;
ASSERT(Timeout != 0);
WaitForBusy(Channel, ATA_STATUS_TIMEOUT);
while (Timeout--)
{
StallExecutionProcessor(10);
Status = AtaReadPort(Channel, IDX_IO1_i_Status);
if (Status & IDE_STATUS_ERROR)
return FALSE;
else if ((Status & Flags) == ExpectedValue)
return TRUE;
}
return FALSE;
}
static
BOOLEAN
WaitForFlagsOr(
IN UCHAR Channel,
IN UCHAR FirstValue,
IN UCHAR SecondValue,
IN ULONG Timeout)
{
UCHAR Status;
ASSERT(Timeout != 0);
WaitForBusy(Channel, ATA_STATUS_TIMEOUT);
while (Timeout--)
{
StallExecutionProcessor(10);
Status = AtaReadPort(Channel, IDX_IO1_i_Status);
if (Status & IDE_STATUS_ERROR)
return FALSE;
else if ((Status & FirstValue) || (Status & SecondValue))
return TRUE;
}
return FALSE;
}
static
BOOLEAN
WaitForBusy(
IN UCHAR Channel,
IN ULONG Timeout)
{ {
ASSERT(Timeout != 0); ASSERT(Timeout != 0);
while (Timeout--) while (Timeout--)
{ {
if ((AtaReadPort(Channel, IDX_IO1_i_Status) & Flags) == ExpectedValue) StallExecutionProcessor(10);
if ((AtaReadPort(Channel, IDX_IO1_i_Status) & IDE_STATUS_BUSY) == 0)
return TRUE; return TRUE;
else
StallExecutionProcessor(10);
} }
return FALSE; return FALSE;
} }
@ -597,31 +724,25 @@ AtaHardReset(IN UCHAR Channel)
TRACE("AtaHardReset(Controller %d)\n", Channel); TRACE("AtaHardReset(Controller %d)\n", Channel);
AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_RESET_CONTROLLER); AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_RESET_CONTROLLER);
StallExecutionProcessor(200000); StallExecutionProcessor(100000);
AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_REENABLE_CONTROLLER); AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_REENABLE_CONTROLLER);
StallExecutionProcessor(1); StallExecutionProcessor(5);
WaitForBusy(Channel, ATA_STATUS_TIMEOUT);
} }
static static
BOOLEAN VOID
SelectDevice(IN UCHAR Channel, IN UCHAR DeviceNumber) SelectDevice(IN UCHAR Channel, IN UCHAR DeviceNumber)
{ {
#if defined(SARCH_PC98) #if defined(SARCH_PC98)
/* Select IDE Channel */ /* Select IDE Channel */
WRITE_PORT_UCHAR((PUCHAR)IDE_IO_o_BankSelect, Channel); WRITE_PORT_UCHAR((PUCHAR)IDE_IO_o_BankSelect, Channel);
StallExecutionProcessor(1); StallExecutionProcessor(5);
#endif #endif
AtaWritePort(Channel, IDX_IO1_o_DriveSelect, AtaWritePort(Channel, IDX_IO1_o_DriveSelect,
DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1); DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1);
StallExecutionProcessor(500); StallExecutionProcessor(5);
if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ), 0, ATA_STATUS_TIMEOUT))
{
TRACE("SelectDevice() failed. Device(%d:%d) is busy.\n", Channel, DeviceNumber);
return FALSE;
}
return TRUE;
} }
static static
@ -631,11 +752,10 @@ IdentifyDevice(
IN UCHAR DeviceNumber, IN UCHAR DeviceNumber,
OUT PDEVICE_UNIT *DeviceUnit) OUT PDEVICE_UNIT *DeviceUnit)
{ {
UCHAR SignatureLow, SignatureHigh; UCHAR SignatureLow, SignatureHigh, SignatureCount, SignatureNumber;
UCHAR Command;
IDENTIFY_DATA Id; IDENTIFY_DATA Id;
INQUIRYDATA AtapiInquiry; SENSE_DATA SenseData;
BOOLEAN Success;
UCHAR AtapiPacket[16];
ULONG i; ULONG i;
ULONG SectorSize; ULONG SectorSize;
ULONGLONG TotalSectors; ULONGLONG TotalSectors;
@ -644,50 +764,61 @@ IdentifyDevice(
TRACE("IdentifyDevice() Channel = %x, Device = %x, BaseIoAddress = 0x%x\n", TRACE("IdentifyDevice() Channel = %x, Device = %x, BaseIoAddress = 0x%x\n",
Channel, DeviceNumber, BaseArray[Channel]); Channel, DeviceNumber, BaseArray[Channel]);
/* Look at controller */
SelectDevice(Channel, DeviceNumber);
StallExecutionProcessor(5);
AtaWritePort(Channel, IDX_IO1_o_BlockNumber, 0x55);
AtaWritePort(Channel, IDX_IO1_o_BlockNumber, 0x55);
StallExecutionProcessor(5);
if (AtaReadPort(Channel, IDX_IO1_i_BlockNumber) != 0x55)
goto Failure;
/* Reset the controller */ /* Reset the controller */
AtaHardReset(Channel); AtaHardReset(Channel);
/* Select the drive */ /* Select the drive */
if (!SelectDevice(Channel, DeviceNumber)) SelectDevice(Channel, DeviceNumber);
if (!WaitForBusy(Channel, ATA_STATUS_TIMEOUT))
goto Failure; goto Failure;
/* Send the IDENTIFY DEVICE command */ /* Signature check */
AtaWritePort(Channel, IDX_IO1_o_Command, IDE_COMMAND_IDENTIFY);
StallExecutionProcessor(50);
if (WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR),
IDE_STATUS_DRQ, ATA_STATUS_TIMEOUT))
{
SignatureLow = AtaReadPort(Channel, IDX_IO1_i_CylinderLow);
SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh);
TRACE("IdentifyDevice(): SignatureLow = 0x%x, SignatureHigh = 0x%x\n", SignatureLow, SignatureHigh);
if (SignatureLow == 0x00 && SignatureHigh == 0x00)
{
/* This is PATA */
TRACE("IdentifyDevice(): Found PATA device at %d:%d\n", Channel, DeviceNumber);
goto Identify;
}
}
/* If not PATA, ATAPI maybe? Send the IDENTIFY PACKET DEVICE command */
AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_IDENTIFY);
StallExecutionProcessor(500);
SignatureLow = AtaReadPort(Channel, IDX_IO1_i_CylinderLow); SignatureLow = AtaReadPort(Channel, IDX_IO1_i_CylinderLow);
SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh); SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh);
TRACE("IdentifyDevice(): SignatureLow = 0x%x, SignatureHigh = 0x%x\n", SignatureLow, SignatureHigh); SignatureCount = AtaReadPort(Channel, IDX_IO1_i_BlockCount);
/* Check for devices that implements the PACKET Command feature */ SignatureNumber = AtaReadPort(Channel, IDX_IO1_i_BlockNumber);
if (SignatureLow == ATAPI_MAGIC_LSB && SignatureHigh == ATAPI_MAGIC_MSB) TRACE("IdentifyDevice(): SL = 0x%x, SH = 0x%x, SC = 0x%x, SN = 0x%x\n",
SignatureLow, SignatureHigh, SignatureCount, SignatureNumber);
if (SignatureLow == 0x00 && SignatureHigh == 0x00 &&
SignatureCount == 0x01 && SignatureNumber == 0x01)
{
TRACE("IdentifyDevice(): Found PATA device at %d:%d\n", Channel, DeviceNumber);
Command = IDE_COMMAND_IDENTIFY;
}
else if (SignatureLow == ATAPI_MAGIC_LSB &&
SignatureHigh == ATAPI_MAGIC_MSB)
{ {
/* This is ATAPI */
Flags |= ATA_DEVICE_ATAPI | ATA_DEVICE_LBA | ATA_DEVICE_NOT_READY;
TRACE("IdentifyDevice(): Found ATAPI device at %d:%d\n", Channel, DeviceNumber); TRACE("IdentifyDevice(): Found ATAPI device at %d:%d\n", Channel, DeviceNumber);
goto Identify; Flags |= ATA_DEVICE_ATAPI | ATA_DEVICE_LBA | ATA_DEVICE_NOT_READY;
Command = IDE_COMMAND_ATAPI_IDENTIFY;
} }
else else
{ {
goto Failure; goto Failure;
} }
Identify: /* Disable interrupts */
AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_DISABLE_INTERRUPTS);
StallExecutionProcessor(5);
/* Send the identify command */
AtaWritePort(Channel, IDX_IO1_o_Command, Command);
StallExecutionProcessor(50);
if (!WaitForFlags(Channel, IDE_STATUS_DRQ, IDE_STATUS_DRQ, ATA_STATUS_TIMEOUT))
{
ERR("IdentifyDevice(): Identify command failed.\n");
goto Failure;
}
/* Receive parameter information from the device */ /* Receive parameter information from the device */
AtaReadBuffer(Channel, &Id, IDENTIFY_DATA_SIZE); AtaReadBuffer(Channel, &Id, IDENTIFY_DATA_SIZE);
@ -705,25 +836,6 @@ Identify:
TRACE("FR %.*s\n", sizeof(Id.FirmwareRevision), Id.FirmwareRevision); TRACE("FR %.*s\n", sizeof(Id.FirmwareRevision), Id.FirmwareRevision);
TRACE("MN %.*s\n", sizeof(Id.ModelNumber), Id.ModelNumber); TRACE("MN %.*s\n", sizeof(Id.ModelNumber), Id.ModelNumber);
/* Detect the type of device */
if (Flags & ATA_DEVICE_ATAPI)
{
/* Send the SCSI INQUIRY command */
RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
AtapiPacket[0] = SCSIOP_INQUIRY;
AtapiPacket[4] = INQUIRYDATABUFFERSIZE;
Success = AtaSendAtapiPacket(Channel, AtapiPacket, ATAPI_PACKET_SIZE(Id), INQUIRYDATABUFFERSIZE);
if (!Success)
goto Failure;
AtaReadBuffer(Channel, &AtapiInquiry, INQUIRYDATABUFFERSIZE);
if (AtapiInquiry.DeviceType != READ_ONLY_DIRECT_ACCESS_DEVICE)
{
TRACE("IdentifyDevice(): Unsupported device type 0x%x!\n", AtapiInquiry.DeviceType);
goto Failure;
}
}
/* Allocate a new device unit structure */ /* Allocate a new device unit structure */
*DeviceUnit = FrLdrTempAlloc(sizeof(DEVICE_UNIT), TAG_ATA_DEVICE); *DeviceUnit = FrLdrTempAlloc(sizeof(DEVICE_UNIT), TAG_ATA_DEVICE);
if (*DeviceUnit == NULL) if (*DeviceUnit == NULL)
@ -737,9 +849,16 @@ Identify:
(*DeviceUnit)->DeviceNumber = DeviceNumber; (*DeviceUnit)->DeviceNumber = DeviceNumber;
(*DeviceUnit)->IdentifyData = Id; (*DeviceUnit)->IdentifyData = Id;
/* Detect a medium's capacity */
if (Flags & ATA_DEVICE_ATAPI) if (Flags & ATA_DEVICE_ATAPI)
{ {
/* Clear the ATAPI 'Bus reset' indication */
for (i = 0; i < 10; ++i)
{
AtapiRequestSense(*DeviceUnit, &SenseData);
StallExecutionProcessor(10);
}
/* Detect a medium's capacity */
AtapiCapacityDetect(*DeviceUnit, &TotalSectors, &SectorSize); AtapiCapacityDetect(*DeviceUnit, &TotalSectors, &SectorSize);
if (SectorSize == 0 || TotalSectors == 0) if (SectorSize == 0 || TotalSectors == 0)
{ {

View file

@ -1,8 +1,8 @@
/* /*
* PROJECT: FreeLoader * PROJECT: FreeLoader
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: ATA/ATAPI polled I/O driver header file. * PURPOSE: ATA/ATAPI programmed I/O driver header file.
* COPYRIGHT: Copyright 2019 Dmitry Borisov (di.sean@protonmail.com) * COPYRIGHT: Copyright 2019-2020 Dmitry Borisov (di.sean@protonmail.com)
*/ */
/* GLOBALS ********************************************************************/ /* GLOBALS ********************************************************************/
@ -35,8 +35,7 @@
#define IDX_IO1_o_DriveSelect 0x0C #define IDX_IO1_o_DriveSelect 0x0C
#define IDX_IO1_o_Command 0x0E #define IDX_IO1_o_Command 0x0E
#define IDX_IO2_o_AltStatus 0x10C #define IDX_IO2_o_Control 0x10C
#define IDX_IO2_o_Control 0x10E
#define IDE_IO_o_BankSelect 0x432 #define IDE_IO_o_BankSelect 0x432
#else /* SARCH_PC98 */ #else /* SARCH_PC98 */
#define IDX_IO1_i_Data 0x00 #define IDX_IO1_i_Data 0x00
@ -60,8 +59,7 @@
#define IDX_IO1_o_DriveSelect 0x06 #define IDX_IO1_o_DriveSelect 0x06
#define IDX_IO1_o_Command 0x07 #define IDX_IO1_o_Command 0x07
#define IDX_IO2_o_AltStatus 0x206 #define IDX_IO2_o_Control 0x206
#define IDX_IO2_o_Control 0x207
#endif #endif
/* /*