Skip to content

Commit a55ceaf

Browse files
authored
[FREELDR:HWIDE] Improve support for ATAPI devices (reactos#8560)
- Adjust DRQ timeout to fix some slow ATAPI devices. - Add a fix for devices that clear BSY before raising the DRQ bit. - Enable use of 32-bit I/O on Xbox. CORE-17977 CORE-16216
1 parent f49ecce commit a55ceaf

File tree

3 files changed

+129
-109
lines changed

3 files changed

+129
-109
lines changed

boot/freeldr/freeldr/arch/drivers/hwide.c

Lines changed: 121 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* PROJECT: FreeLoader
33
* LICENSE: MIT (https://spdx.org/licenses/MIT)
44
* PURPOSE: ATA/ATAPI programmed I/O driver.
5-
* COPYRIGHT: Copyright 2019-2025 Dmitry Borisov ([email protected])
5+
* COPYRIGHT: Copyright 2019-2026 Dmitry Borisov <[email protected]>
66
*/
77

88
/* INCLUDES *******************************************************************/
@@ -38,21 +38,6 @@ static PHW_DEVICE_UNIT AtapUnits[CHANNEL_MAX_CHANNELS * CHANNEL_MAX_DEVICES];
3838

3939
/* PRIVATE FUNCTIONS **********************************************************/
4040

41-
#if defined(ATA_SUPPORT_32_BIT_IO)
42-
static
43-
inline
44-
BOOLEAN
45-
AtapIs32BitIoSupported(
46-
_In_ PHW_DEVICE_UNIT DeviceUnit)
47-
{
48-
#if defined(ATA_ALWAYS_DO_32_BIT_IO)
49-
return TRUE;
50-
#else
51-
return !!(DeviceUnit->P.Flags & ATA_DEVICE_FLAG_IO32);
52-
#endif
53-
}
54-
#endif
55-
5641
static
5742
VOID
5843
AtapSelectDevice(
@@ -69,11 +54,12 @@ AtapSelectDevice(
6954
}
7055

7156
static
72-
BOOLEAN
73-
AtapWaitForNotBusy(
57+
UCHAR
58+
AtapWait(
7459
_In_ PIDE_REGISTERS Registers,
7560
_In_range_(>, 0) ULONG Timeout,
76-
_Out_opt_ PUCHAR Result)
61+
_In_ UCHAR Mask,
62+
_In_ UCHAR Value)
7763
{
7864
UCHAR IdeStatus;
7965
ULONG i;
@@ -83,47 +69,16 @@ AtapWaitForNotBusy(
8369
for (i = 0; i < Timeout; ++i)
8470
{
8571
IdeStatus = ATA_READ(Registers->Status);
86-
if (!(IdeStatus & IDE_STATUS_BUSY))
87-
{
88-
if (Result)
89-
*Result = IdeStatus;
90-
return TRUE;
91-
}
72+
if ((IdeStatus & Mask) == Value)
73+
break;
9274

9375
if (IdeStatus == 0xFF)
9476
break;
9577

9678
StallExecutionProcessor(10);
9779
}
9880

99-
if (Result)
100-
*Result = IdeStatus;
101-
return FALSE;
102-
}
103-
104-
static
105-
BOOLEAN
106-
AtapWaitForIdle(
107-
_In_ PIDE_REGISTERS Registers,
108-
_Out_ PUCHAR Result)
109-
{
110-
UCHAR IdeStatus;
111-
ULONG i;
112-
113-
for (i = 0; i < ATA_TIME_DRQ_CLEAR; ++i)
114-
{
115-
IdeStatus = ATA_READ(Registers->Status);
116-
if (!(IdeStatus & (IDE_STATUS_DRQ | IDE_STATUS_BUSY)))
117-
{
118-
*Result = IdeStatus;
119-
return TRUE;
120-
}
121-
122-
StallExecutionProcessor(2);
123-
}
124-
125-
*Result = IdeStatus;
126-
return FALSE;
81+
return IdeStatus;
12782
}
12883

12984
static
@@ -133,40 +88,14 @@ AtapSendCdb(
13388
_In_ PATA_DEVICE_REQUEST Request)
13489
{
13590
#if defined(ATA_SUPPORT_32_BIT_IO)
136-
if (AtapIs32BitIoSupported(DeviceUnit))
137-
{
138-
ATA_WRITE_BLOCK_32(DeviceUnit->Registers.Data,
139-
Request->Cdb,
140-
DeviceUnit->CdbSize / sizeof(USHORT));
141-
}
142-
else
91+
ATA_WRITE_BLOCK_32(DeviceUnit->Registers.Data,
92+
Request->Cdb,
93+
DeviceUnit->CdbSize / sizeof(USHORT));
94+
#else
95+
ATA_WRITE_BLOCK_16(DeviceUnit->Registers.Data,
96+
Request->Cdb,
97+
DeviceUnit->CdbSize);
14398
#endif
144-
{
145-
ATA_WRITE_BLOCK_16(DeviceUnit->Registers.Data,
146-
Request->Cdb,
147-
DeviceUnit->CdbSize);
148-
}
149-
150-
/*
151-
* In polled mode (interrupts disabled)
152-
* the NEC CDR-260 drive clears BSY before updating the interrupt reason register.
153-
* As a workaround, we will wait for the phase change.
154-
*/
155-
if (DeviceUnit->P.Flags & ATA_DEVICE_IS_NEC_CDR260)
156-
{
157-
ULONG i;
158-
159-
ATA_IO_WAIT();
160-
161-
for (i = 0; i < ATA_TIME_PHASE_CHANGE; ++i)
162-
{
163-
UCHAR InterruptReason = ATA_READ(DeviceUnit->Registers.InterruptReason);
164-
if (InterruptReason != ATAPI_INT_REASON_COD)
165-
break;
166-
167-
StallExecutionProcessor(10);
168-
}
169-
}
17099
}
171100

172101
static
@@ -178,7 +107,7 @@ AtapPioDataIn(
178107
ByteCount = min(ByteCount, DeviceUnit->BytesToTransfer);
179108

180109
#if defined(ATA_SUPPORT_32_BIT_IO)
181-
if (AtapIs32BitIoSupported(DeviceUnit))
110+
if (!(ByteCount & (sizeof(ULONG) - 1)))
182111
{
183112
ATA_READ_BLOCK_32(DeviceUnit->Registers.Data,
184113
(PULONG)DeviceUnit->DataBuffer,
@@ -233,15 +162,37 @@ AtapSoftwareReset(
233162
StallExecutionProcessor(20);
234163
}
235164

165+
static
166+
VOID
167+
AtapDrainDeviceBuffer(
168+
_In_ PIDE_REGISTERS Registers)
169+
{
170+
UCHAR IdeStatus;
171+
ULONG i;
172+
173+
/* Try to clear the DRQ indication */
174+
for (i = 0; i < 0x10000 / sizeof(USHORT); ++i)
175+
{
176+
IdeStatus = ATA_READ(Registers->Status);
177+
if (!(IdeStatus & IDE_STATUS_DRQ))
178+
break;
179+
180+
READ_PORT_USHORT((PUSHORT)(ULONG_PTR)Registers->Data);
181+
}
182+
}
183+
236184
static
237185
BOOLEAN
238186
AtapPerformSoftwareReset(
239187
_In_ PHW_DEVICE_UNIT DeviceUnit)
240188
{
241189
PIDE_REGISTERS Registers = &DeviceUnit->Registers;
190+
UCHAR IdeStatus;
242191

243192
ERR("Reset device at %X:%u\n", Registers->Data, DeviceUnit->DeviceNumber);
244193

194+
AtapDrainDeviceBuffer(Registers);
195+
245196
/* Perform a software reset */
246197
AtapSoftwareReset(Registers);
247198

@@ -253,7 +204,8 @@ AtapPerformSoftwareReset(
253204
}
254205

255206
/* Now wait for busy to clear */
256-
if (!AtapWaitForNotBusy(Registers, ATA_TIME_BUSY_RESET, NULL))
207+
IdeStatus = AtapWait(Registers, ATA_TIME_BUSY_RESET, IDE_STATUS_BUSY, 0);
208+
if (IdeStatus & IDE_STATUS_BUSY)
257209
return FALSE;
258210

259211
return TRUE;
@@ -272,6 +224,23 @@ AtapProcessAtapiRequest(
272224
InterruptReason &= ATAPI_INT_REASON_MASK;
273225
InterruptReason |= IdeStatus & IDE_STATUS_DRQ;
274226

227+
/*
228+
* The NEC CDR-C251 drive is not fully ATAPI-compliant
229+
* and clears BSY before raising the DRQ bit and updating the interrupt reason register.
230+
* As a workaround, we will wait a bit more in the case the valid IR is not quite there yet.
231+
*/
232+
if ((InterruptReason == ATAPI_INT_REASON_COD) || (InterruptReason == ATAPI_INT_REASON_IO))
233+
{
234+
IdeStatus = AtapWait(&DeviceUnit->Registers,
235+
ATA_TIME_DRQ_ASSERT,
236+
IDE_STATUS_DRQ,
237+
IDE_STATUS_DRQ);
238+
239+
InterruptReason = ATA_READ(DeviceUnit->Registers.InterruptReason);
240+
InterruptReason &= ATAPI_INT_REASON_MASK;
241+
InterruptReason |= IdeStatus & IDE_STATUS_DRQ;
242+
}
243+
275244
switch (InterruptReason)
276245
{
277246
case ATAPI_INT_REASON_AWAIT_CDB:
@@ -341,8 +310,20 @@ AtapProcessAtaRequest(
341310
/* Read command */
342311
if (Request->DataBuffer)
343312
{
313+
/*
314+
* The NEC CDR-C251 drive clears BSY before raising the DRQ bit
315+
* while processing the ATAPI identify command.
316+
* Give the device a chance to assert that bit.
317+
*/
344318
if (!(IdeStatus & IDE_STATUS_DRQ))
345-
return ATA_STATUS_RESET;
319+
{
320+
IdeStatus = AtapWait(&DeviceUnit->Registers,
321+
ATA_TIME_DRQ_ASSERT,
322+
IDE_STATUS_DRQ,
323+
IDE_STATUS_DRQ);
324+
if (!(IdeStatus & IDE_STATUS_DRQ))
325+
return ATA_STATUS_RESET;
326+
}
346327

347328
/* Read the next data block */
348329
AtapPioDataIn(DeviceUnit, DeviceUnit->DrqByteCount);
@@ -351,7 +332,11 @@ AtapProcessAtaRequest(
351332
return ATA_STATUS_PENDING;
352333

353334
/* All data has been transferred, wait for DRQ to clear */
354-
if (!AtapWaitForIdle(&DeviceUnit->Registers, &IdeStatus))
335+
IdeStatus = AtapWait(&DeviceUnit->Registers,
336+
ATA_TIME_DRQ_CLEAR,
337+
IDE_STATUS_BUSY | IDE_STATUS_DRQ,
338+
0);
339+
if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))
355340
return ATA_STATUS_RESET;
356341

357342
if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT))
@@ -363,16 +348,49 @@ AtapProcessAtaRequest(
363348
}
364349

365350
static
366-
BOOLEAN
351+
UCHAR
367352
AtapProcessRequest(
368353
_In_ PHW_DEVICE_UNIT DeviceUnit,
369354
_In_ PATA_DEVICE_REQUEST Request,
370355
_In_ UCHAR IdeStatus)
371356
{
372-
if (Request->Flags & REQUEST_FLAG_PACKET_COMMAND)
373-
return AtapProcessAtapiRequest(DeviceUnit, Request, IdeStatus);
374-
else
357+
UCHAR AtaStatus;
358+
359+
if (!(Request->Flags & REQUEST_FLAG_PACKET_COMMAND))
375360
return AtapProcessAtaRequest(DeviceUnit, Request, IdeStatus);
361+
362+
AtaStatus = AtapProcessAtapiRequest(DeviceUnit, Request, IdeStatus);
363+
364+
/*
365+
* In polled mode (interrupts disabled), the NEC CDR-260 drive behaves quite differently
366+
* that other devices. This drive does not raise BSY immediately
367+
* in response to a CDB or buffer write. The status and interrupt reason registers
368+
* remain invalid and unchanged for the time of media access.
369+
* As a workaround, we will wait for the phase change.
370+
*/
371+
if ((DeviceUnit->P.Flags & ATA_DEVICE_IS_NEC_CDR260) && (AtaStatus == ATA_STATUS_PENDING))
372+
{
373+
UCHAR OldInterruptReason, NewInterruptReason;
374+
ULONG i;
375+
376+
OldInterruptReason = ATA_READ(DeviceUnit->Registers.InterruptReason);
377+
378+
/* Set a long timeout on purpose */
379+
for (i = ATA_TIME_BUSY_POLL; i > 0; i--)
380+
{
381+
NewInterruptReason = ATA_READ(DeviceUnit->Registers.InterruptReason);
382+
if (NewInterruptReason != OldInterruptReason)
383+
break;
384+
385+
StallExecutionProcessor(10);
386+
}
387+
if (i == 0)
388+
{
389+
AtaStatus = ATA_STATUS_RESET;
390+
}
391+
}
392+
393+
return AtaStatus;
376394
}
377395

378396
static
@@ -438,14 +456,15 @@ AtapSendCommand(
438456
_In_ PHW_DEVICE_UNIT DeviceUnit,
439457
_In_ PATA_DEVICE_REQUEST Request)
440458
{
441-
UCHAR AtaStatus;
459+
UCHAR IdeStatus, AtaStatus;
442460

443461
DeviceUnit->BytesToTransfer = Request->DataTransferLength;
444462
DeviceUnit->DataBuffer = Request->DataBuffer;
445463

446464
/* Select the device */
447465
AtapSelectDevice(&DeviceUnit->Registers, DeviceUnit->DeviceNumber);
448-
if (!AtapWaitForNotBusy(&DeviceUnit->Registers, ATA_TIME_BUSY_SELECT, NULL))
466+
IdeStatus = AtapWait(&DeviceUnit->Registers, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0);
467+
if (IdeStatus & IDE_STATUS_BUSY)
449468
return ATA_STATUS_RETRY;
450469

451470
/* Always disable interrupts, otherwise it may lead to problems in underlying BIOS firmware */
@@ -458,11 +477,10 @@ AtapSendCommand(
458477

459478
while (TRUE)
460479
{
461-
UCHAR IdeStatus;
462-
463480
ATA_IO_WAIT();
464481

465-
if (!AtapWaitForNotBusy(&DeviceUnit->Registers, ATA_TIME_BUSY_POLL, &IdeStatus))
482+
IdeStatus = AtapWait(&DeviceUnit->Registers, ATA_TIME_BUSY_POLL, IDE_STATUS_BUSY, 0);
483+
if (IdeStatus & IDE_STATUS_BUSY)
466484
return ATA_STATUS_RESET;
467485

468486
AtaStatus = AtapProcessRequest(DeviceUnit, Request, IdeStatus);
@@ -555,10 +573,6 @@ AtapIssueCommand(
555573
return FALSE;
556574
}
557575

558-
/* Turn off various things and retry the command */
559-
DeviceUnit->MultiSectorTransfer = 0;
560-
DeviceUnit->P.Flags &= ~ATA_DEVICE_FLAG_IO32;
561-
562576
if (!AtapPerformSoftwareReset(DeviceUnit))
563577
return FALSE;
564578

@@ -738,7 +752,8 @@ AtapIsDevicePresent(
738752
if (ATA_READ(Registers->ByteCountHigh) != 0xAA)
739753
return FALSE;
740754

741-
if (!AtapWaitForNotBusy(Registers, ATA_TIME_BUSY_ENUM, &IdeStatus))
755+
IdeStatus = AtapWait(&DeviceUnit->Registers, ATA_TIME_BUSY_ENUM, IDE_STATUS_BUSY, 0);
756+
if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))
742757
{
743758
ERR("Device %X:%u is busy %02x\n", Registers->Data, DeviceUnit->DeviceNumber, IdeStatus);
744759

@@ -1090,7 +1105,7 @@ AtapAtaInitDevice(
10901105
else
10911106
{
10921107
/* Using CHS addressing mode */
1093-
TotalSectors = Cylinders * Heads * SectorsPerTrack;
1108+
TotalSectors = (ULONG64)Cylinders * Heads * SectorsPerTrack;
10941109
}
10951110

10961111
if (TotalSectors == 0)
@@ -1221,6 +1236,7 @@ AtaReadLogicalSectors(
12211236
PHW_DEVICE_UNIT Unit = (PHW_DEVICE_UNIT)DeviceUnit;
12221237
ATA_DEVICE_REQUEST Request = { 0 };
12231238

1239+
ASSERT(Unit);
12241240
ASSERT((SectorNumber + SectorCount) <= Unit->P.TotalSectors);
12251241
ASSERT(SectorCount != 0);
12261242

0 commit comments

Comments
 (0)