From: Dmitry Borisov Date: Sun, 22 Dec 2019 21:52:40 +0000 (+0600) Subject: [FREELDR] Add ATA/ATAPI driver. (#2167) X-Git-Tag: 0.4.14-RC~760 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=9a12473280931a85ea2587044b1adfb9f1b87b2f [FREELDR] Add ATA/ATAPI driver. (#2167) CORE-16220 CORE-16216 * Create a new driver. * Use the functions from the library in xboxdisk.c Driver now supports PC, Xbox and NEC PC-98. Co-Authored-By: Hermès Bélusca-Maïto --- diff --git a/boot/freeldr/freeldr/CMakeLists.txt b/boot/freeldr/freeldr/CMakeLists.txt index d4086a7893b..37f44004ef8 100644 --- a/boot/freeldr/freeldr/CMakeLists.txt +++ b/boot/freeldr/freeldr/CMakeLists.txt @@ -66,6 +66,7 @@ list(APPEND FREELDR_NTLDR_SOURCE list(APPEND FREELDR_ARC_SOURCE arcname.c + arch/drivers/hwide.c arch/arcemul.c arch/archwsup.c disk/disk.c diff --git a/boot/freeldr/freeldr/arch/drivers/hwide.c b/boot/freeldr/freeldr/arch/drivers/hwide.c new file mode 100644 index 00000000000..51f59857823 --- /dev/null +++ b/boot/freeldr/freeldr/arch/drivers/hwide.c @@ -0,0 +1,822 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: ATA/ATAPI polled I/O driver. + * COPYRIGHT: Copyright 2019 Dmitry Borisov (di.sean@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include +#include + +/* DDK */ +#include +#include + +#include +DBG_DEFAULT_CHANNEL(DISK); + +/* GLOBALS ********************************************************************/ + +#define TAG_ATA_DEVICE 'DatA' +#define ATAPI_PACKET_SIZE(IdentifyData) (IdentifyData.AtapiCmdSize ? 16 : 12) + +/* 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) \ + WRITE_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port)), (Data)) + +#define AtaReadPort(Channel, Port) \ + READ_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port))) + +#define AtaWriteBuffer(Channel, Buffer, Count) \ + WRITE_PORT_BUFFER_USHORT(UlongToPtr(BaseArray[(Channel)] + IDX_IO1_o_Data), \ + (PUSHORT)(Buffer), (Count)/sizeof(USHORT)) + +#define AtaReadBuffer(Channel, Buffer, Count) \ + READ_PORT_BUFFER_USHORT(UlongToPtr(BaseArray[(Channel)] + IDX_IO1_i_Data), \ + (PUSHORT)(Buffer), (Count)/sizeof(USHORT)) + +/* IDE/ATA Channels base - Primary, Secondary, Tertiary, Quaternary */ +static const ULONG BaseArray[] = +{ +#if defined(SARCH_XBOX) + 0x1F0 +#elif defined(SARCH_PC98) + 0x640, 0x640 +#else + 0x1F0, 0x170, 0x1E8, 0x168 +#endif +}; + +#define MAX_CHANNELS RTL_NUMBER_OF(BaseArray) +#define MAX_DEVICES 2 /* Master/Slave */ + +static PDEVICE_UNIT Units[MAX_CHANNELS * MAX_DEVICES]; + +/* PRIVATE PROTOTYPES *********************************************************/ + +static +BOOLEAN +WaitForFlags( + IN UCHAR Channel, + IN UCHAR Flags, + IN UCHAR ExpectedValue, + IN ULONG Timeout +); + +static +BOOLEAN +SelectDevice( + IN UCHAR Channel, + IN UCHAR DeviceNumber +); + +static +BOOLEAN +IdentifyDevice( + IN UCHAR Channel, + IN UCHAR DeviceNumber, + OUT PDEVICE_UNIT *DeviceUnit +); + +static +BOOLEAN +AtapiReadyCheck( + IN OUT PDEVICE_UNIT DeviceUnit +); + +static +BOOLEAN +AtapiReadLogicalSectorLBA( + IN PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + OUT PVOID Buffer +); + +static +BOOLEAN +AtaReadLogicalSectorsLBA( + IN PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + IN ULONG SectorCount, + OUT PVOID Buffer +); + +/* FUNCTIONS ******************************************************************/ + +/* Don't call this before running the system timer calibration and MM initialization */ +BOOLEAN +AtaInit(OUT PUCHAR DetectedCount) +{ + UCHAR Channel, DeviceNumber; + PDEVICE_UNIT DeviceUnit = NULL; + + TRACE("AtaInit()\n"); + + *DetectedCount = 0; + + RtlZeroMemory(&Units, sizeof(Units)); + + /* Detect and enumerate ATA/ATAPI devices */ + for (Channel = 0; Channel < MAX_CHANNELS; ++Channel) + { + for (DeviceNumber = 0; DeviceNumber < MAX_DEVICES; ++DeviceNumber) + { + if (IdentifyDevice(Channel, DeviceNumber, &DeviceUnit)) + { + Units[(*DetectedCount)++] = DeviceUnit; + } + } + } + + return (*DetectedCount > 0); +} + +VOID +AtaFree(VOID) +{ + UCHAR i; + + for (i = 0; i < RTL_NUMBER_OF(Units); ++i) + { + if (Units[i]) + FrLdrTempFree(Units[i], TAG_ATA_DEVICE); + } +} + +PDEVICE_UNIT +AtaGetDevice(IN UCHAR UnitNumber) +{ + if (UnitNumber < RTL_NUMBER_OF(Units)) + return Units[UnitNumber]; + else + return NULL; +} + +BOOLEAN +AtaAtapiReadLogicalSectorsLBA( + IN OUT PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + IN ULONG SectorCount, + OUT PVOID Buffer) +{ + UCHAR RetryCount; + BOOLEAN Success; + + if (DeviceUnit == NULL || SectorCount == 0) + return FALSE; + + if (DeviceUnit->Flags & ATA_DEVICE_ATAPI) + { + if ((DeviceUnit->Flags & ATA_DEVICE_NO_MEDIA) || (DeviceUnit->Flags & ATA_DEVICE_NOT_READY)) + { + /* Retry 4 times */ + for (RetryCount = 0; RetryCount < 4; ++RetryCount) + { + /* Make the device ready */ + if (AtapiReadyCheck(DeviceUnit)) + break; + } + if (RetryCount >= 4) + { + ERR("AtaAtapiReadLogicalSectorsLBA(): Device not ready.\n"); + return FALSE; + } + } + if (SectorNumber + SectorCount > DeviceUnit->TotalSectors + 1) + { + ERR("AtaAtapiReadLogicalSectorsLBA(): Attempt to read more than there is to read.\n"); + return FALSE; + } + + while (SectorCount > 0) + { + /* Read a single sector */ + Success = AtapiReadLogicalSectorLBA(DeviceUnit, SectorNumber, Buffer); + if (!Success) + return FALSE; + + --SectorCount; + ++SectorNumber; + Buffer = (PVOID)((ULONG_PTR)Buffer + DeviceUnit->SectorSize); + } + } + else + { + /* Retry 3 times */ + for (RetryCount = 0; RetryCount < 3; ++RetryCount) + { + /* Read a multiple sectors */ + Success = AtaReadLogicalSectorsLBA(DeviceUnit, SectorNumber, SectorCount, Buffer); + if (Success) + return TRUE; + } + return FALSE; + } + + return TRUE; +} + +static +BOOLEAN +AtaReadLogicalSectorsLBA( + IN PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + IN ULONG SectorCount, + OUT PVOID Buffer) +{ + UCHAR Command; + ULONG ChsTemp; + USHORT Cylinder; + UCHAR Head; + UCHAR Sector; + ULONG BlockCount; + ULONG RemainingBlockCount; + ULONGLONG Lba; + BOOLEAN UseLBA48; + + UseLBA48 = (DeviceUnit->Flags & ATA_DEVICE_LBA48) && + (((SectorNumber + SectorCount) >= UINT64_C(0x0FFFFF80)) || SectorCount > 256); + + while (SectorCount > 0) + { + /* Prevent sector count overflow, divide it into maximum possible chunks and loop each one */ + if (UseLBA48) + BlockCount = min(SectorCount, USHRT_MAX); + else + BlockCount = min(SectorCount, UCHAR_MAX); + + /* Convert LBA into a format CHS if needed */ + if (DeviceUnit->Flags & ATA_DEVICE_CHS) + { + ChsTemp = DeviceUnit->IdentifyData.SectorsPerTrack * DeviceUnit->IdentifyData.NumberOfHeads; + if (ChsTemp) + { + Cylinder = SectorNumber / ChsTemp; + Head = (SectorNumber % ChsTemp) / DeviceUnit->IdentifyData.SectorsPerTrack; + Sector = (SectorNumber % DeviceUnit->IdentifyData.SectorsPerTrack) + 1; + } + else + { + Cylinder = 0; + Head = 0; + Sector = 1; + } + Lba = (Sector & 0xFF) | ((Cylinder & 0xFFFFF) << 8) | ((Head & 0x0F) << 24); + } + else + { + Lba = SectorNumber; + } + + /* Select the drive */ + if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) + return FALSE; + + /* Disable interrupts */ +#ifndef SARCH_PC98 + AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS); + StallExecutionProcessor(1); +#endif + + if (UseLBA48) + { + /* FIFO */ + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, 0); + 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 & 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_CylinderLow, (Lba >> 32) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderLow, (Lba >> 8) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 40) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 16) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_DriveSelect, + IDE_USE_LBA | (DeviceUnit->DeviceNumber ? IDE_DRIVE_2 : IDE_DRIVE_1)); + Command = IDE_COMMAND_READ_EXT; + } + else + { + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, ATA_PIO); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, BlockCount & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, Lba & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderLow, (Lba >> 8) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 16) & 0xFF); + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_DriveSelect, + ((Lba >> 24) & 0x0F) | + (DeviceUnit->Flags & ATA_DEVICE_CHS ? 0x00 : IDE_USE_LBA) | + (DeviceUnit->DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1)); + Command = IDE_COMMAND_READ; + } + + /* Send read command */ + AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Command, Command); + StallExecutionProcessor(5); + + for (RemainingBlockCount = BlockCount; RemainingBlockCount > 0; --RemainingBlockCount) + { + /* Wait for ready to transfer data block */ + if (!WaitForFlags(DeviceUnit->Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR), + IDE_STATUS_DRQ, ATA_READ_TIMEOUT)) + { + ERR("AtaReadLogicalSectorsLBA() failed. Status: 0x%02x, Error: 0x%02x\n", + AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Status), + AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Error)); + return FALSE; + } + + /* Transfer the data block */ + AtaReadBuffer(DeviceUnit->Channel, Buffer, DeviceUnit->SectorSize); + + Buffer = (PVOID)((ULONG_PTR)Buffer + DeviceUnit->SectorSize); + } + + SectorNumber += BlockCount; + SectorCount -= BlockCount; + } + + return TRUE; +} + +static +BOOLEAN +AtaSendAtapiPacket( + IN UCHAR Channel, + IN PUCHAR AtapiPacket, + IN UCHAR PacketSize, + IN USHORT ByteCount) +{ + /* No DRQ for TEST UNIT READY */ + BOOLEAN NoData = (AtapiPacket[0] == SCSIOP_TEST_UNIT_READY); + UCHAR ExpectedFlags = NoData ? IDE_STATUS_DRDY : (IDE_STATUS_DRQ | IDE_STATUS_DRDY); + + /* PIO mode */ + AtaWritePort(Channel, IDX_ATAPI_IO1_o_Feature, ATA_PIO); + + /* Maximum byte count that is to be transferred */ + AtaWritePort(Channel, IDX_ATAPI_IO1_o_ByteCountLow, ByteCount & 0xFF); + AtaWritePort(Channel, IDX_ATAPI_IO1_o_ByteCountHigh, (ByteCount >> 8) & 0xFF); + + /* Prepare to transfer a device command via a command packet */ + AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET); + StallExecutionProcessor(50); + if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR), + 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", + AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error)); + return FALSE; + } + + /* Command packet transfer */ + AtaWriteBuffer(Channel, AtapiPacket, PacketSize); + if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_DRDY | IDE_STATUS_ERROR), + ExpectedFlags, NoData ? ATA_STATUS_TIMEOUT : ATA_READ_TIMEOUT)) + { + 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)); + return FALSE; + } + + return TRUE; +} + +static +BOOLEAN +AtapiReadLogicalSectorLBA( + IN PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + OUT PVOID Buffer) +{ + UCHAR AtapiPacket[16]; + USHORT DataSize; + BOOLEAN Success; + + /* Select the drive */ + if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) + return FALSE; + + /* Disable interrupts */ + AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS); + StallExecutionProcessor(1); + + /* Send the SCSI READ command */ + RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket)); +#if defined(SARCH_PC98) + 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[3] = (SectorNumber >> 16) & 0xFF; + AtapiPacket[4] = (SectorNumber >> 8) & 0xFF; + AtapiPacket[5] = SectorNumber & 0xFF; + Success = AtaSendAtapiPacket(DeviceUnit->Channel, + AtapiPacket, + ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), + DeviceUnit->SectorSize); + if (!Success) + { + ERR("AtapiReadLogicalSectorLBA() failed. A read error occurred.\n"); + return FALSE; + } + + DataSize = (AtaReadPort(DeviceUnit->Channel, IDX_ATAPI_IO1_i_ByteCountHigh) << 8) | + AtaReadPort(DeviceUnit->Channel, IDX_ATAPI_IO1_i_ByteCountLow); + + /* Transfer the data block */ + AtaReadBuffer(DeviceUnit->Channel, Buffer, DataSize); + + return TRUE; +} + +static +VOID +AtapiCapacityDetect( + IN PDEVICE_UNIT DeviceUnit, + OUT PULONGLONG TotalSectors, + OUT PULONG SectorSize) +{ + UCHAR AtapiPacket[16]; + UCHAR AtapiCapacity[8]; + + /* Send the SCSI READ CAPACITY(10) command */ + RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket)); + AtapiPacket[0] = SCSIOP_READ_CAPACITY; + if (AtaSendAtapiPacket(DeviceUnit->Channel, AtapiPacket, ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), 8)) + { + AtaReadBuffer(DeviceUnit->Channel, &AtapiCapacity, 8); + + *TotalSectors = (AtapiCapacity[0] << 24) | (AtapiCapacity[1] << 16) | + (AtapiCapacity[2] << 8) | AtapiCapacity[3]; + + *SectorSize = (AtapiCapacity[4] << 24) | (AtapiCapacity[5] << 16) | + (AtapiCapacity[6] << 8) | AtapiCapacity[7]; + } + else + { + *TotalSectors = 0; + *SectorSize = 0; + } +} + +static +BOOLEAN +AtapiReadyCheck(IN OUT PDEVICE_UNIT DeviceUnit) +{ + UCHAR AtapiPacket[16]; + UCHAR DummyData[MAXIMUM_CDROM_SIZE]; + SENSE_DATA SenseData; + BOOLEAN Success; + + /* Select the drive */ + if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber)) + return FALSE; + + /* Send the SCSI TEST UNIT READY command */ + RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket)); + AtapiPacket[0] = SCSIOP_TEST_UNIT_READY; + AtaSendAtapiPacket(DeviceUnit->Channel, + AtapiPacket, + ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), + 0); + + /* Send the SCSI REQUEST SENSE command */ + 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; + + AtaReadBuffer(DeviceUnit->Channel, &SenseData, SENSE_BUFFER_SIZE); + TRACE("SK 0x%x, ASC 0x%x, ASCQ 0x%x\n", + SenseData.SenseKey, + SenseData.AdditionalSenseCode, + SenseData.AdditionalSenseCodeQualifier); + + if (SenseData.SenseKey == SCSI_SENSE_NOT_READY) + { + if (SenseData.AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) + { + switch (SenseData.AdditionalSenseCodeQualifier) + { + case SCSI_SENSEQ_BECOMING_READY: + /* Wait until the CD is spun up */ + StallExecutionProcessor(4e6); + return FALSE; + + case SCSI_SENSEQ_INIT_COMMAND_REQUIRED: + /* The drive needs to be spun up, send the SCSI READ TOC command */ + RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket)); + AtapiPacket[0] = SCSIOP_READ_TOC; + AtapiPacket[7] = (MAXIMUM_CDROM_SIZE << 8) & 0xFF; + AtapiPacket[8] = MAXIMUM_CDROM_SIZE & 0xFF; + AtapiPacket[9] = READ_TOC_FORMAT_SESSION << 6; + Success = AtaSendAtapiPacket(DeviceUnit->Channel, + AtapiPacket, + ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), + MAXIMUM_CDROM_SIZE); + if (!Success) + return FALSE; + + AtaReadBuffer(DeviceUnit->Channel, &DummyData, MAXIMUM_CDROM_SIZE); + /* fall through */ + + default: + DeviceUnit->Flags &= ~ATA_DEVICE_NOT_READY; + return FALSE; + + } + } + else if (SenseData.AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) + { + DeviceUnit->Flags |= ATA_DEVICE_NO_MEDIA; + return FALSE; + } + } + else + { + DeviceUnit->Flags &= ~ATA_DEVICE_NOT_READY; + } + + if (DeviceUnit->Flags & ATA_DEVICE_NO_MEDIA) + { + /* Detect a medium's capacity */ + AtapiCapacityDetect(DeviceUnit, &DeviceUnit->TotalSectors, &DeviceUnit->SectorSize); + + /* If nothing was returned, reset to defaults */ + if (DeviceUnit->SectorSize == 0) + DeviceUnit->SectorSize = 2048; + if (DeviceUnit->TotalSectors == 0) + DeviceUnit->TotalSectors = 0xFFFFFFFF; + + DeviceUnit->Flags &= ~ATA_DEVICE_NO_MEDIA; + } + + return TRUE; +} + +static +BOOLEAN +WaitForFlags( + IN UCHAR Channel, + IN UCHAR Flags, + IN UCHAR ExpectedValue, + IN ULONG Timeout) +{ + ASSERT(Timeout != 0); + + while (Timeout--) + { + if ((AtaReadPort(Channel, IDX_IO1_i_Status) & Flags) == ExpectedValue) + return TRUE; + else + StallExecutionProcessor(10); + } + return FALSE; +} + +static +VOID +AtaHardReset(IN UCHAR Channel) +{ + TRACE("AtaHardReset(Controller %d)\n", Channel); + + AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_RESET_CONTROLLER); + StallExecutionProcessor(200000); + AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_REENABLE_CONTROLLER); + StallExecutionProcessor(1); +} + +static +BOOLEAN +SelectDevice(IN UCHAR Channel, IN UCHAR DeviceNumber) +{ +#if defined(SARCH_PC98) + /* Select IDE Channel */ + WRITE_PORT_UCHAR((PUCHAR)IDE_IO_o_BankSelect, Channel); + StallExecutionProcessor(1); +#endif + + AtaWritePort(Channel, IDX_IO1_o_DriveSelect, + DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1); + StallExecutionProcessor(500); + 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 +BOOLEAN +IdentifyDevice( + IN UCHAR Channel, + IN UCHAR DeviceNumber, + OUT PDEVICE_UNIT *DeviceUnit) +{ + UCHAR SignatureLow, SignatureHigh; + IDENTIFY_DATA Id; + INQUIRYDATA AtapiInquiry; + BOOLEAN Success; + UCHAR AtapiPacket[16]; + ULONG i; + ULONG SectorSize; + ULONGLONG TotalSectors; + USHORT Flags = 0; + + TRACE("IdentifyDevice() Channel = %x, Device = %x, BaseIoAddress = 0x%x\n", + Channel, DeviceNumber, BaseArray[Channel]); + + /* Reset the controller */ + AtaHardReset(Channel); + + /* Select the drive */ + if (!SelectDevice(Channel, DeviceNumber)) + goto Failure; + + /* Send the IDENTIFY DEVICE command */ + 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); + SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh); + TRACE("IdentifyDevice(): SignatureLow = 0x%x, SignatureHigh = 0x%x\n", SignatureLow, SignatureHigh); + /* Check for devices that implements the PACKET Command feature */ + 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); + goto Identify; + } + else + { + goto Failure; + } + +Identify: + /* Receive parameter information from the device */ + AtaReadBuffer(Channel, &Id, IDENTIFY_DATA_SIZE); + + /* Swap byte order of the ASCII data */ + for (i = 0; i < RTL_NUMBER_OF(Id.SerialNumber); ++i) + Id.SerialNumber[i] = RtlUshortByteSwap(Id.SerialNumber[i]); + + for (i = 0; i < RTL_NUMBER_OF(Id.FirmwareRevision); ++i) + Id.FirmwareRevision[i] = RtlUshortByteSwap(Id.FirmwareRevision[i]); + + for (i = 0; i < RTL_NUMBER_OF(Id.ModelNumber); ++i) + Id.ModelNumber[i] = RtlUshortByteSwap(Id.ModelNumber[i]); + + TRACE("S/N %.*s\n", sizeof(Id.SerialNumber), Id.SerialNumber); + TRACE("FR %.*s\n", sizeof(Id.FirmwareRevision), Id.FirmwareRevision); + 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 */ + *DeviceUnit = FrLdrTempAlloc(sizeof(DEVICE_UNIT), TAG_ATA_DEVICE); + if (*DeviceUnit == NULL) + { + ERR("Failed to allocate device unit!\n"); + return FALSE; + } + + RtlZeroMemory(*DeviceUnit, sizeof(DEVICE_UNIT)); + (*DeviceUnit)->Channel = Channel; + (*DeviceUnit)->DeviceNumber = DeviceNumber; + (*DeviceUnit)->IdentifyData = Id; + + /* Detect a medium's capacity */ + if (Flags & ATA_DEVICE_ATAPI) + { + AtapiCapacityDetect(*DeviceUnit, &TotalSectors, &SectorSize); + if (SectorSize == 0 || TotalSectors == 0) + { + /* It's ok and can be used to show alert like "Please insert the CD" */ + TRACE("No media found.\n"); + Flags |= ATA_DEVICE_NO_MEDIA; + } + } + else + { + if (Id.SupportLba || (Id.MajorRevision && Id.UserAddressableSectors)) + { + if (Id.FeaturesSupport.Address48) + { + TRACE("Using LBA48 addressing mode.\n"); + Flags |= ATA_DEVICE_LBA48 | ATA_DEVICE_LBA; + TotalSectors = Id.UserAddressableSectors48; + } + else + { + TRACE("Using LBA28 addressing mode.\n"); + Flags |= ATA_DEVICE_LBA; + TotalSectors = Id.UserAddressableSectors; + } + + /* LBA ATA drives always have a sector size of 512 */ + SectorSize = 512; + } + else + { + TRACE("Using CHS addressing mode.\n"); + Flags |= ATA_DEVICE_CHS; + + if (Id.UnformattedBytesPerSector == 0) + { + SectorSize = 512; + } + else + { + for (i = 1 << 15; i > 0; i >>= 1) + { + if ((Id.UnformattedBytesPerSector & i) != 0) + { + SectorSize = i; + break; + } + } + } + TotalSectors = Id.NumberOfCylinders * Id.NumberOfHeads * Id.SectorsPerTrack; + } + } + TRACE("Sector size %d ; Total sectors %I64d\n", SectorSize, TotalSectors); + + (*DeviceUnit)->Flags = Flags; + (*DeviceUnit)->SectorSize = SectorSize; + (*DeviceUnit)->TotalSectors = TotalSectors; + if (Flags & ATA_DEVICE_ATAPI) + { + (*DeviceUnit)->Cylinders = 0xFFFFFFFF; + (*DeviceUnit)->Heads = 0xFFFFFFFF; + (*DeviceUnit)->Sectors = 0xFFFFFFFF; + } + else + { + (*DeviceUnit)->Cylinders = Id.NumberOfCylinders; + (*DeviceUnit)->Heads = Id.NumberOfHeads; + (*DeviceUnit)->Sectors = Id.SectorsPerTrack; + } + +#if DBG + DbgDumpBuffer(DPRINT_DISK, &Id, IDENTIFY_DATA_SIZE); +#endif + + TRACE("IdentifyDevice() done.\n"); + return TRUE; + +Failure: + TRACE("IdentifyDevice() done. No device present at %d:%d\n", Channel, DeviceNumber); + return FALSE; +} diff --git a/boot/freeldr/freeldr/arch/i386/machxbox.c b/boot/freeldr/freeldr/arch/i386/machxbox.c index 7b12084c294..ed99abbb72a 100644 --- a/boot/freeldr/freeldr/arch/i386/machxbox.c +++ b/boot/freeldr/freeldr/arch/i386/machxbox.c @@ -299,8 +299,9 @@ XboxMachInit(const char *CmdLine) VOID XboxPrepareForReactOS(VOID) { - /* On XBOX, prepare video and turn off the floppy motor */ + /* On Xbox, prepare video and disk support */ XboxVideoPrepareForReactOS(); + XboxDiskInit(FALSE); DiskStopFloppyMotor(); } diff --git a/boot/freeldr/freeldr/arch/i386/xboxdisk.c b/boot/freeldr/freeldr/arch/i386/xboxdisk.c index e1e4d3559e7..26a00e1136c 100644 --- a/boot/freeldr/freeldr/arch/i386/xboxdisk.c +++ b/boot/freeldr/freeldr/arch/i386/xboxdisk.c @@ -1,513 +1,120 @@ /* - * FreeLoader - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Note: mostly ripped from atapi.c - * Some of this code was based on knowledge and/or code developed - * by the Xbox Linux group: http://www.xbox-linux.org - * + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Xbox specific disk access routines + * COPYRIGHT: Copyright 2004 Gé van Geldorp (gvg@reactos.com) + * Copyright 2019 Dmitry Borisov (di.sean@protonmail.com) */ +/* INCLUDES *******************************************************************/ + #include +#include #include - DBG_DEFAULT_CHANNEL(DISK); -#define XBOX_IDE_COMMAND_PORT 0x1f0 -#define XBOX_IDE_CONTROL_PORT 0x170 - -#define IDE_SECTOR_BUF_SZ 512 -#define IDE_MAX_POLL_RETRIES 100000 -#define IDE_MAX_BUSY_RETRIES 50000 - -/* Control Block offsets and masks */ -#define IDE_REG_ALT_STATUS 0x0000 -#define IDE_REG_DEV_CNTRL 0x0000 /* device control register */ -#define IDE_DC_SRST 0x04 /* drive reset (both drives) */ -#define IDE_DC_nIEN 0x02 /* IRQ enable (active low) */ -#define IDE_REG_DRV_ADDR 0x0001 - -/* Command Block offsets and masks */ -#define IDE_REG_DATA_PORT 0x0000 -#define IDE_REG_ERROR 0x0001 /* error register */ -#define IDE_ER_AMNF 0x01 /* addr mark not found */ -#define IDE_ER_TK0NF 0x02 /* track 0 not found */ -#define IDE_ER_ABRT 0x04 /* command aborted */ -#define IDE_ER_MCR 0x08 /* media change requested */ -#define IDE_ER_IDNF 0x10 /* ID not found */ -#define IDE_ER_MC 0x20 /* Media changed */ -#define IDE_ER_UNC 0x40 /* Uncorrectable data error */ -#define IDE_REG_PRECOMP 0x0001 -#define IDE_REG_SECTOR_CNT 0x0002 -#define IDE_REG_SECTOR_NUM 0x0003 -#define IDE_REG_CYL_LOW 0x0004 -#define IDE_REG_CYL_HIGH 0x0005 -#define IDE_REG_DRV_HEAD 0x0006 -#define IDE_DH_FIXED 0xA0 -#define IDE_DH_LBA 0x40 -#define IDE_DH_HDMASK 0x0F -#define IDE_DH_DRV0 0x00 -#define IDE_DH_DRV1 0x10 -#define IDE_REG_STATUS 0x0007 -#define IDE_SR_BUSY 0x80 -#define IDE_SR_DRDY 0x40 -#define IDE_SR_WERR 0x20 -#define IDE_SR_DRQ 0x08 -#define IDE_SR_ERR 0x01 -#define IDE_REG_COMMAND 0x0007 - -/* IDE/ATA commands */ -#define IDE_CMD_RESET 0x08 -#define IDE_CMD_READ 0x20 -#define IDE_CMD_READ_RETRY 0x21 -#define IDE_CMD_WRITE 0x30 -#define IDE_CMD_WRITE_RETRY 0x31 -#define IDE_CMD_PACKET 0xA0 -#define IDE_CMD_READ_MULTIPLE 0xC4 -#define IDE_CMD_WRITE_MULTIPLE 0xC5 -#define IDE_CMD_READ_DMA 0xC8 -#define IDE_CMD_WRITE_DMA 0xCA -#define IDE_CMD_FLUSH_CACHE 0xE7 -#define IDE_CMD_FLUSH_CACHE_EXT 0xEA -#define IDE_CMD_IDENT_ATA_DRV 0xEC -#define IDE_CMD_IDENT_ATAPI_DRV 0xA1 -#define IDE_CMD_GET_MEDIA_STATUS 0xDA - -/* - * Access macros for command registers - * Each macro takes an address of the command port block, and data - */ -#define IDEReadError(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR))) -#define IDEWritePrecomp(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data))) -#define IDEReadSectorCount(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT))) -#define IDEWriteSectorCount(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data))) -#define IDEReadSectorNum(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM))) -#define IDEWriteSectorNum(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data))) -#define IDEReadCylinderLow(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW))) -#define IDEWriteCylinderLow(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data))) -#define IDEReadCylinderHigh(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH))) -#define IDEWriteCylinderHigh(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data))) -#define IDEReadDriveHead(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD))) -#define IDEWriteDriveHead(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data))) -#define IDEReadStatus(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS))) -#define IDEWriteCommand(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data))) -#define IDEReadDMACommand(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address)))) -#define IDEWriteDMACommand(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address)), (Data))) -#define IDEReadDMAStatus(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + 2))) -#define IDEWriteDMAStatus(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + 2), (Data))) -#define IDEWritePRDTable(Address, Data) \ - (WRITE_PORT_ULONG((PULONG)((Address) + 4), (Data))) - -/* - * Data block read and write commands - */ -#define IDEReadBlock(Address, Buffer, Count) \ - (READ_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) -#define IDEWriteBlock(Address, Buffer, Count) \ - (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) - -#define IDEReadBlock32(Address, Buffer, Count) \ - (READ_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4)) -#define IDEWriteBlock32(Address, Buffer, Count) \ - (WRITE_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4)) +/* GLOBALS ********************************************************************/ -#define IDEReadWord(Address) \ - (READ_PORT_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT))) +static PDEVICE_UNIT HardDrive = NULL; +static PDEVICE_UNIT CdDrive = NULL; +static BOOLEAN AtaInitialized = FALSE; -/* - * Access macros for control registers - * Each macro takes an address of the control port blank and data - */ -#define IDEReadAltStatus(Address) \ - (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ALT_STATUS))) -#define IDEWriteDriveControl(Address, Data) \ - (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data))) +/* FUNCTIONS ******************************************************************/ -/* IDE_DRIVE_IDENTIFY */ -typedef struct _IDE_DRIVE_IDENTIFY +VOID +XboxDiskInit(BOOLEAN Init) { - USHORT ConfigBits; /*00*/ - USHORT LogicalCyls; /*01*/ - USHORT Reserved02; /*02*/ - USHORT LogicalHeads; /*03*/ - USHORT BytesPerTrack; /*04*/ - USHORT BytesPerSector; /*05*/ - USHORT SectorsPerTrack; /*06*/ - UCHAR InterSectorGap; /*07*/ - UCHAR InterSectorGapSize; - UCHAR Reserved08H; /*08*/ - UCHAR BytesInPLO; - USHORT VendorUniqueCnt; /*09*/ - UCHAR SerialNumber[20]; /*10*/ - USHORT ControllerType; /*20*/ - USHORT BufferSize; /*21*/ - USHORT ECCByteCnt; /*22*/ - UCHAR FirmwareRev[8]; /*23*/ - UCHAR ModelNumber[40]; /*27*/ - USHORT RWMultImplemented; /*47*/ - USHORT DWordIo; /*48*/ - USHORT Capabilities; /*49*/ -#define IDE_DRID_STBY_SUPPORTED 0x2000 -#define IDE_DRID_IORDY_SUPPORTED 0x0800 -#define IDE_DRID_IORDY_DISABLE 0x0400 -#define IDE_DRID_LBA_SUPPORTED 0x0200 -#define IDE_DRID_DMA_SUPPORTED 0x0100 - USHORT Reserved50; /*50*/ - USHORT MinPIOTransTime; /*51*/ - USHORT MinDMATransTime; /*52*/ - USHORT TMFieldsValid; /*53*/ - USHORT TMCylinders; /*54*/ - USHORT TMHeads; /*55*/ - USHORT TMSectorsPerTrk; /*56*/ - USHORT TMCapacityLo; /*57*/ - USHORT TMCapacityHi; /*58*/ - USHORT RWMultCurrent; /*59*/ - USHORT TMSectorCountLo; /*60*/ - USHORT TMSectorCountHi; /*61*/ - USHORT DmaModes; /*62*/ - USHORT MultiDmaModes; /*63*/ - USHORT Reserved64[5]; /*64*/ - USHORT Reserved69[2]; /*69*/ - USHORT Reserved71[4]; /*71*/ - USHORT MaxQueueDepth; /*75*/ - USHORT Reserved76[4]; /*76*/ - USHORT MajorRevision; /*80*/ - USHORT MinorRevision; /*81*/ - USHORT SupportedFeatures82; /*82*/ - USHORT SupportedFeatures83; /*83*/ - USHORT SupportedFeatures84; /*84*/ - USHORT EnabledFeatures85; /*85*/ - USHORT EnabledFeatures86; /*86*/ - USHORT EnabledFeatures87; /*87*/ - USHORT UltraDmaModes; /*88*/ - USHORT Reserved89[11]; /*89*/ - USHORT Max48BitAddress[4]; /*100*/ - USHORT Reserved104[151]; /*104*/ - USHORT Checksum; /*255*/ -} IDE_DRIVE_IDENTIFY, *PIDE_DRIVE_IDENTIFY; - -/* XboxDiskPolledRead - * - * DESCRIPTION: - * Read a sector of data from the drive in a polled fashion. - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * ULONG CommandPort Address of command port for drive - * ULONG ControlPort Address of control port for drive - * UCHAR PreComp Value to write to precomp register - * UCHAR SectorCnt Value to write to sectorCnt register - * UCHAR SectorNum Value to write to sectorNum register - * UCHAR CylinderLow Value to write to CylinderLow register - * UCHAR CylinderHigh Value to write to CylinderHigh register - * UCHAR DrvHead Value to write to Drive/Head register - * UCHAR Command Value to write to Command register - * PVOID Buffer Buffer for output data - * - * RETURNS: - * BOOLEAN: TRUE success, FALSE error - */ -static BOOLEAN -XboxDiskPolledRead(ULONG CommandPort, - ULONG ControlPort, - UCHAR PreComp, - UCHAR SectorCnt, - UCHAR SectorNum, - UCHAR CylinderLow, - UCHAR CylinderHigh, - UCHAR DrvHead, - UCHAR Command, - PVOID Buffer) -{ - ULONG SectorCount = 0; - ULONG RetryCount; - BOOLEAN Junk = FALSE; - UCHAR Status; - - /* Wait for BUSY to clear */ - for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) - break; - - StallExecutionProcessor(10); - } - TRACE("status=0x%x\n", Status); - TRACE("waited %d usecs for busy to clear\n", RetryCount * 10); - if (RetryCount >= IDE_MAX_BUSY_RETRIES) - { - WARN("Drive is BUSY for too long\n"); - return FALSE; - } - - /* Write Drive/Head to select drive */ - IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead); - StallExecutionProcessor(500); - - /* Disable interrupts */ - IDEWriteDriveControl(ControlPort, IDE_DC_nIEN); - StallExecutionProcessor(500); - - /* Issue command to drive */ - if (DrvHead & IDE_DH_LBA) - { - TRACE("READ:DRV=%d:LBA=1:BLK=%d:SC=0x%x:CM=0x%x\n", - DrvHead & IDE_DH_DRV1 ? 1 : 0, - ((DrvHead & 0x0f) << 24) + (CylinderHigh << 16) + (CylinderLow << 8) + SectorNum, - SectorCnt, - Command); - } - else - { - TRACE("READ:DRV=%d:LBA=0:CH=0x%x:CL=0x%x:HD=0x%x:SN=0x%x:SC=0x%x:CM=0x%x\n", - DrvHead & IDE_DH_DRV1 ? 1 : 0, - CylinderHigh, - CylinderLow, - DrvHead & 0x0f, - SectorNum, - SectorCnt, - Command); - } - - /* Setup command parameters */ - IDEWritePrecomp(CommandPort, PreComp); - IDEWriteSectorCount(CommandPort, SectorCnt); - IDEWriteSectorNum(CommandPort, SectorNum); - IDEWriteCylinderHigh(CommandPort, CylinderHigh); - IDEWriteCylinderLow(CommandPort, CylinderLow); - IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead); - - /* Issue the command */ - IDEWriteCommand(CommandPort, Command); - StallExecutionProcessor(50); - - /* Wait for DRQ or error */ - for (RetryCount = 0; RetryCount < IDE_MAX_POLL_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) - { - if (Status & IDE_SR_ERR) - { - IDEWriteDriveControl(ControlPort, 0); - StallExecutionProcessor(50); - IDEReadStatus(CommandPort); - return FALSE; - } - - if (Status & IDE_SR_DRQ) - { - break; - } - else - { - IDEWriteDriveControl(ControlPort, 0); - StallExecutionProcessor(50); - IDEReadStatus(CommandPort); - return FALSE; - } - } - StallExecutionProcessor(10); - } - - /* Timed out */ - if (RetryCount >= IDE_MAX_POLL_RETRIES) - { - IDEWriteDriveControl(ControlPort, 0); - StallExecutionProcessor(50); - IDEReadStatus(CommandPort); - return FALSE; - } + UCHAR DetectedCount; + UCHAR UnitNumber; + PDEVICE_UNIT DeviceUnit = NULL; - while (1) + if (Init & !AtaInitialized) { - /* Read data into buffer */ - if (Junk == FALSE) + /* Find first HDD and CD */ + AtaInit(&DetectedCount); + for (UnitNumber = 0; UnitNumber <= DetectedCount; UnitNumber++) { - IDEReadBlock(CommandPort, Buffer, IDE_SECTOR_BUF_SZ); - Buffer = (PVOID)((ULONG_PTR)Buffer + IDE_SECTOR_BUF_SZ); - } - else - { - UCHAR JunkBuffer[IDE_SECTOR_BUF_SZ]; - IDEReadBlock(CommandPort, JunkBuffer, IDE_SECTOR_BUF_SZ); - } - SectorCount++; - - /* Check for error or more sectors to read */ - for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) + DeviceUnit = AtaGetDevice(UnitNumber); + if (DeviceUnit) { - if (Status & IDE_SR_ERR) - { - IDEWriteDriveControl(ControlPort, 0); - StallExecutionProcessor(50); - IDEReadStatus(CommandPort); - return FALSE; - } - if (Status & IDE_SR_DRQ) + if (DeviceUnit->Flags & ATA_DEVICE_ATAPI) { - if (SectorCount >= SectorCnt) - { - TRACE("Buffer size exceeded!\n"); - Junk = TRUE; - } - break; + if (!CdDrive) + CdDrive = DeviceUnit; } else { - if (SectorCount > SectorCnt) - { - TRACE("Read %lu sectors of junk!\n", - SectorCount - SectorCnt); - } - - IDEWriteDriveControl(ControlPort, 0); - StallExecutionProcessor(50); - IDEReadStatus(CommandPort); - return TRUE; + if (!HardDrive) + HardDrive = DeviceUnit; } } } + AtaInitialized = TRUE; + } + else + { + AtaFree(); } } +static +inline BOOLEAN -XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) +XboxDiskDriveNumberToDeviceUnit(UCHAR DriveNumber, PDEVICE_UNIT *DeviceUnit) { - ULONG StartSector; - UCHAR Count; - - if (DriveNumber < 0x80 || (DriveNumber & 0x0f) >= 2) - { - /* Xbox has only 1 IDE controller and no floppy */ - WARN("Invalid drive number\n"); + /* Xbox has only 1 IDE controller and no floppy */ + if (DriveNumber < 0x80 || (DriveNumber & 0x0F) >= 2) return FALSE; - } - if (((SectorNumber + SectorCount) & UINT64_C(0xfffffffff0000000)) != UINT64_C(0)) + if (!AtaInitialized) + XboxDiskInit(TRUE); + + /* HDD */ + if ((DriveNumber == 0x80) && HardDrive) { - FIXME("48bit LBA required but not implemented\n"); - return FALSE; + *DeviceUnit = HardDrive; + return TRUE; } - StartSector = (ULONG) SectorNumber; - while (SectorCount > 0) + /* CD */ + if ((DriveNumber & 0xF0) > 0x80 && CdDrive) { - Count = (SectorCount <= 255 ? (UCHAR)SectorCount : 255); - if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT, - XBOX_IDE_CONTROL_PORT, - 0, Count, - StartSector & 0xff, - (StartSector >> 8) & 0xff, - (StartSector >> 16) & 0xff, - ((StartSector >> 24) & 0x0f) | IDE_DH_LBA | - ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1), - IDE_CMD_READ, - Buffer)) - { - return FALSE; - } - SectorCount -= Count; - Buffer = (PVOID) ((PCHAR) Buffer + Count * IDE_SECTOR_BUF_SZ); + *DeviceUnit = CdDrive; + return TRUE; } - return TRUE; + return FALSE; } BOOLEAN -XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry) +XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) { - IDE_DRIVE_IDENTIFY DrvParms; - ULONG i; - BOOLEAN Atapi; + PDEVICE_UNIT DeviceUnit = NULL; - Atapi = FALSE; /* FIXME */ - /* Get the Drive Identify block from drive or die */ - if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT, - XBOX_IDE_CONTROL_PORT, - 0, - 1, - 0, - 0, - 0, - ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1), - (Atapi ? IDE_CMD_IDENT_ATAPI_DRV : IDE_CMD_IDENT_ATA_DRV), - (PUCHAR) &DrvParms)) - { - ERR("XboxDiskPolledRead() failed\n"); + TRACE("XboxDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n", + DriveNumber, SectorNumber, SectorCount, Buffer); + + if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit)) return FALSE; - } - Geometry->Cylinders = DrvParms.LogicalCyls; - Geometry->Heads = DrvParms.LogicalHeads; - Geometry->Sectors = DrvParms.SectorsPerTrack; + return AtaAtapiReadLogicalSectorsLBA(DeviceUnit, SectorNumber, SectorCount, Buffer); +} - if (!Atapi && (DrvParms.Capabilities & IDE_DRID_LBA_SUPPORTED) != 0) - { - /* LBA ATA drives always have a sector size of 512 */ - Geometry->BytesPerSector = 512; - } - else - { - TRACE("BytesPerSector %d\n", DrvParms.BytesPerSector); - if (DrvParms.BytesPerSector == 0) - { - Geometry->BytesPerSector = 512; - } - else - { - for (i = 1 << 15; i; i /= 2) - { - if ((DrvParms.BytesPerSector & i) != 0) - { - Geometry->BytesPerSector = i; - break; - } - } - } - } - TRACE("Cylinders %d\n", Geometry->Cylinders); - TRACE("Heads %d\n", Geometry->Heads); - TRACE("Sectors %d\n", Geometry->Sectors); - TRACE("BytesPerSector %d\n", Geometry->BytesPerSector); +BOOLEAN +XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry) +{ + PDEVICE_UNIT DeviceUnit = NULL; + + TRACE("XboxDiskGetDriveGeometry(0x%x)\n", DriveNumber); + + if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit)) + return FALSE; + + Geometry->Cylinders = DeviceUnit->Cylinders; + Geometry->Heads = DeviceUnit->Heads; + Geometry->Sectors = DeviceUnit->Sectors; + Geometry->BytesPerSector = DeviceUnit->SectorSize; return TRUE; } @@ -515,8 +122,19 @@ XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry) ULONG XboxDiskGetCacheableBlockCount(UCHAR DriveNumber) { - /* 64 seems a nice number, it is used by the machpc code for LBA devices */ - return 64; -} + PDEVICE_UNIT DeviceUnit = NULL; + + TRACE("XboxDiskGetCacheableBlockCount(0x%x)\n", DriveNumber); -/* EOF */ + if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit)) + return 0; + + /* + * If LBA is supported then the block size will be 64 sectors (32k) + * If not then the block size is the size of one track. + */ + if (DeviceUnit->Flags & ATA_DEVICE_LBA) + return 64; + else + return DeviceUnit->Sectors; +} diff --git a/boot/freeldr/freeldr/include/arch/i386/machxbox.h b/boot/freeldr/freeldr/include/arch/i386/machxbox.h index 31f8f7e996d..4b85e9b59d3 100644 --- a/boot/freeldr/freeldr/include/arch/i386/machxbox.h +++ b/boot/freeldr/freeldr/include/arch/i386/machxbox.h @@ -74,6 +74,7 @@ VOID XboxPrepareForReactOS(VOID); VOID XboxMemInit(VOID); PFREELDR_MEMORY_DESCRIPTOR XboxMemGetMemoryMap(ULONG *MemoryMapSize); +VOID XboxDiskInit(BOOLEAN Init); BOOLEAN XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer); BOOLEAN XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY DriveGeometry); ULONG XboxDiskGetCacheableBlockCount(UCHAR DriveNumber); diff --git a/boot/freeldr/freeldr/include/hwide.h b/boot/freeldr/freeldr/include/hwide.h new file mode 100644 index 00000000000..04f9f2d51ae --- /dev/null +++ b/boot/freeldr/freeldr/include/hwide.h @@ -0,0 +1,333 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: ATA/ATAPI polled I/O driver header file. + * COPYRIGHT: Copyright 2019 Dmitry Borisov (di.sean@protonmail.com) + */ + +/* GLOBALS ********************************************************************/ + +/* Some definitions were taken from UniATA driver by Alter */ + +/* + * IDE registers offsets + */ +#if defined(SARCH_PC98) +#define IDX_IO1_i_Data 0x00 +#define IDX_IO1_i_Error 0x02 +#define IDX_IO1_i_BlockCount 0x04 +#define IDX_IO1_i_BlockNumber 0x06 +#define IDX_IO1_i_CylinderLow 0x08 +#define IDX_IO1_i_CylinderHigh 0x0A +#define IDX_IO1_i_DriveSelect 0x0C +#define IDX_IO1_i_Status 0x0E + +#define IDX_IO2_i_AltStatus 0x10C +#define IDX_IO2_i_DriveAddress 0x10E +#define IDE_IO_i_Bank 0x432 + +#define IDX_IO1_o_Data 0x00 +#define IDX_IO1_o_Feature 0x02 +#define IDX_IO1_o_BlockCount 0x04 +#define IDX_IO1_o_BlockNumber 0x06 +#define IDX_IO1_o_CylinderLow 0x08 +#define IDX_IO1_o_CylinderHigh 0x0A +#define IDX_IO1_o_DriveSelect 0x0C +#define IDX_IO1_o_Command 0x0E + +#define IDX_IO2_o_AltStatus 0x10C +#define IDX_IO2_o_Control 0x10E +#define IDE_IO_o_BankSelect 0x432 +#else /* SARCH_PC98 */ +#define IDX_IO1_i_Data 0x00 +#define IDX_IO1_i_Error 0x01 +#define IDX_IO1_i_BlockCount 0x02 +#define IDX_IO1_i_BlockNumber 0x03 +#define IDX_IO1_i_CylinderLow 0x04 +#define IDX_IO1_i_CylinderHigh 0x05 +#define IDX_IO1_i_DriveSelect 0x06 +#define IDX_IO1_i_Status 0x07 + +#define IDX_IO2_i_AltStatus 0x206 +#define IDX_IO2_i_DriveAddress 0x207 + +#define IDX_IO1_o_Data 0x00 +#define IDX_IO1_o_Feature 0x01 +#define IDX_IO1_o_BlockCount 0x02 +#define IDX_IO1_o_BlockNumber 0x03 +#define IDX_IO1_o_CylinderLow 0x04 +#define IDX_IO1_o_CylinderHigh 0x05 +#define IDX_IO1_o_DriveSelect 0x06 +#define IDX_IO1_o_Command 0x07 + +#define IDX_IO2_o_AltStatus 0x206 +#define IDX_IO2_o_Control 0x207 +#endif + +/* + * ATAPI registers offsets + */ +#if defined(SARCH_PC98) +#define IDX_ATAPI_IO1_i_Data 0x00 +#define IDX_ATAPI_IO1_i_Error 0x02 +#define IDX_ATAPI_IO1_i_InterruptReason 0x04 +#define IDX_ATAPI_IO1_i_Unused1 0x06 +#define IDX_ATAPI_IO1_i_ByteCountLow 0x08 +#define IDX_ATAPI_IO1_i_ByteCountHigh 0x0A +#define IDX_ATAPI_IO1_i_DriveSelect 0x0C +#define IDX_ATAPI_IO1_i_Status 0x0E + +#define IDX_ATAPI_IO1_o_Data 0x00 +#define IDX_ATAPI_IO1_o_Feature 0x02 +#define IDX_ATAPI_IO1_o_Unused0 0x04 +#define IDX_ATAPI_IO1_o_Unused1 0x06 +#define IDX_ATAPI_IO1_o_ByteCountLow 0x08 +#define IDX_ATAPI_IO1_o_ByteCountHigh 0x0A +#define IDX_ATAPI_IO1_o_DriveSelect 0x0C +#define IDX_ATAPI_IO1_o_Command 0x0E +#else /* SARCH_PC98 */ +#define IDX_ATAPI_IO1_i_Data 0x00 +#define IDX_ATAPI_IO1_i_Error 0x01 +#define IDX_ATAPI_IO1_i_InterruptReason 0x02 +#define IDX_ATAPI_IO1_i_Unused1 0x03 +#define IDX_ATAPI_IO1_i_ByteCountLow 0x04 +#define IDX_ATAPI_IO1_i_ByteCountHigh 0x05 +#define IDX_ATAPI_IO1_i_DriveSelect 0x06 +#define IDX_ATAPI_IO1_i_Status 0x07 + +#define IDX_ATAPI_IO1_o_Data 0x00 +#define IDX_ATAPI_IO1_o_Feature 0x01 +#define IDX_ATAPI_IO1_o_Unused0 0x02 +#define IDX_ATAPI_IO1_o_Unused1 0x03 +#define IDX_ATAPI_IO1_o_ByteCountLow 0x04 +#define IDX_ATAPI_IO1_o_ByteCountHigh 0x05 +#define IDX_ATAPI_IO1_o_DriveSelect 0x06 +#define IDX_ATAPI_IO1_o_Command 0x07 +#endif + +/* + * IDE status definitions + */ +#define IDE_STATUS_SUCCESS 0x00 +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_DMA 0x20 /* DMA ready */ +#define IDE_STATUS_DWF 0x20 /* drive write fault */ +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_IDLE 0x50 +#define IDE_STATUS_BUSY 0x80 + +#define IDE_STATUS_WRONG 0xff +#define IDE_STATUS_MASK 0xff + +/* + * IDE drive select/head definitions + */ +#define IDE_DRIVE_SELECT 0xA0 +#define IDE_DRIVE_1 0x00 +#define IDE_DRIVE_2 0x10 +#define IDE_DRIVE_SELECT_1 (IDE_DRIVE_SELECT | IDE_DRIVE_1) +#define IDE_DRIVE_SELECT_2 (IDE_DRIVE_SELECT | IDE_DRIVE_2) +#define IDE_DRIVE_MASK (IDE_DRIVE_SELECT_1 | IDE_DRIVE_SELECT_2) +#define IDE_USE_LBA 0x40 + +/* + * IDE drive control definitions + */ +#define IDE_DC_DISABLE_INTERRUPTS 0x02 +#define IDE_DC_RESET_CONTROLLER 0x04 +#define IDE_DC_A_4BIT 0x80 +#define IDE_DC_USE_HOB 0x80 // use high-order byte(s) +#define IDE_DC_REENABLE_CONTROLLER 0x00 + +/* + * IDE error definitions + */ +#define IDE_ERROR_ICRC 0x80 +#define IDE_ERROR_BAD_BLOCK 0x80 +#define IDE_ERROR_DATA_ERROR 0x40 +#define IDE_ERROR_MEDIA_CHANGE 0x20 +#define IDE_ERROR_ID_NOT_FOUND 0x10 +#define IDE_ERROR_MEDIA_CHANGE_REQ 0x08 +#define IDE_ERROR_COMMAND_ABORTED 0x04 +#define IDE_ERROR_END_OF_MEDIA 0x02 +#define IDE_ERROR_NO_MEDIA 0x02 +#define IDE_ERROR_ILLEGAL_LENGTH 0x01 + +/* + * Values for TransferMode + */ +#define ATA_PIO 0x00 + +/* + * IDENTIFY data + */ +#include +typedef struct _IDENTIFY_DATA +{ + UCHAR AtapiCmdSize:2; // 00 00 General configuration + UCHAR Unused1:3; + UCHAR DrqType:2; + UCHAR Removable:1; + UCHAR DeviceType:5; + UCHAR Unused2:1; + UCHAR CmdProtocol:2; + USHORT NumberOfCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumberOfHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT SectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + USHORT SerialNumber[10]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + USHORT FirmwareRevision[4]; // 2E 23-26 + USHORT ModelNumber[20]; // 36 27-46 + UCHAR ReadWriteMultipleSupport; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Reserved62_0:8; // 62 49 Capabilities + USHORT SupportDma:1; + USHORT SupportLba:1; + USHORT DisableIordy:1; + USHORT SupportIordy:1; + USHORT SoftReset:1; + USHORT StandbyOverlap:1; + USHORT SupportQTag:1; + USHORT SupportIDma:1; + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A 53 + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport:8; // 62 + USHORT SingleWordDMAActive:8; + USHORT MultiWordDMASupport:8; // 63 + USHORT MultiWordDMAActive:8; + USHORT AdvancedPIOModes:8; // 64 + USHORT Reserved4:8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[2]; // 69-70 + USHORT ReleaseTimeOverlapped; // 71 + USHORT ReleaseTimeServiceCommand; // 72 + USHORT Reserved73_74[2]; // 73-74 + USHORT QueueLength:5; // 75 + USHORT Reserved75_6:11; + USHORT SataCapabilities; // 76 + USHORT Reserved77; // 77 + USHORT SataSupport; // 78 + USHORT SataEnable; // 79 + USHORT MajorRevision; // 80 + USHORT MinorRevision; // 81 + struct { + USHORT Smart:1; // 82 + USHORT Security:1; + USHORT Removable:1; + USHORT PowerMngt:1; + USHORT Packet:1; + USHORT WriteCache:1; + USHORT LookAhead:1; + USHORT ReleaseDRQ:1; + USHORT ServiceDRQ:1; + USHORT Reset:1; + USHORT Protected:1; + USHORT Reserved_82_11:1; + USHORT WriteBuffer:1; + USHORT ReadBuffer:1; + USHORT Nop:1; + USHORT Reserved_82_15:1; + USHORT Microcode:1; // 83/86 + USHORT Queued:1; + USHORT CFA:1; + USHORT APM:1; + USHORT Notify:1; + USHORT Standby:1; + USHORT Spinup:1; + USHORT Reserver_83_7:1; + USHORT MaxSecurity:1; + USHORT AutoAcoustic:1; + USHORT Address48:1; + USHORT ConfigOverlay:1; + USHORT FlushCache:1; + USHORT FlushCache48:1; + USHORT SupportOne:1; + USHORT SupportZero:1; + USHORT SmartErrorLog:1; // 84/87 + USHORT SmartSelfTest:1; + USHORT MediaSerialNo:1; + USHORT MediaCardPass:1; + USHORT Streaming:1; + USHORT Logging:1; + USHORT Reserver_84_6:8; + USHORT ExtendedOne:1; + USHORT ExtendedZero:1; + } FeaturesSupport, FeaturesEnabled; + USHORT Reserved6[13]; // 88-99 + ULONGLONG UserAddressableSectors48; // 100-103 + USHORT Reserved7[151]; // 104-255 +} IDENTIFY_DATA, *PIDENTIFY_DATA; +#include +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + +#define ATAPI_MAGIC_LSB 0x14 +#define ATAPI_MAGIC_MSB 0xEB +#define MAXIMUM_CDROM_SIZE 804 + +typedef struct _DEVICE_UNIT +{ + UCHAR Channel; + UCHAR DeviceNumber; + ULONG Cylinders; + ULONG Heads; + ULONG Sectors; + ULONG SectorSize; + ULONGLONG TotalSectors; /* This number starts from 0 */ + USHORT Flags; + IDENTIFY_DATA IdentifyData; +} DEVICE_UNIT, *PDEVICE_UNIT; + +#define ATA_DEVICE_ATAPI (1 << 0) +#define ATA_DEVICE_NO_MEDIA (1 << 1) +#define ATA_DEVICE_NOT_READY (1 << 2) +#define ATA_DEVICE_LBA48 (1 << 3) +#define ATA_DEVICE_LBA (1 << 4) +#define ATA_DEVICE_CHS (1 << 5) + +/* PROTOTYPES ****************************************************************/ + +BOOLEAN +AtaInit( + OUT PUCHAR DetectedCount +); + +VOID +AtaFree(); + +PDEVICE_UNIT +AtaGetDevice( + IN UCHAR UnitNumber +); + +BOOLEAN +AtaAtapiReadLogicalSectorsLBA( + IN OUT PDEVICE_UNIT DeviceUnit, + IN ULONGLONG SectorNumber, + IN ULONG SectorCount, + OUT PVOID Buffer +);