Skip to content

Commit 787f81f

Browse files
authored
[FREELDR] Refactor and optimize the IDE driver (reactos#7728)
[DDK] Update IDENTIFY data and other ATA definitions. Based on the MinGW header. [FREELDR] Refactor and optimize the IDE driver. Fix long-standing bugs, which have a negative impact on the boot stability. - Make the driver more ATA specification compliant. - Improve the speed of device detection. - Remove inconsistent delays. - Support modern hard drives with sector size greater than 512 bytes. - Add basic error recovery. - Move private definitions to a separate private header. - Remove the useless AtaFree API method.
1 parent ddf55b3 commit 787f81f

File tree

10 files changed

+2002
-1097
lines changed

10 files changed

+2002
-1097
lines changed

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

Lines changed: 1068 additions & 681 deletions
Large diffs are not rendered by default.
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
/*
2+
* PROJECT: FreeLoader
3+
* LICENSE: MIT (https://spdx.org/licenses/MIT)
4+
* PURPOSE: Private header for ATA/ATAPI programmed I/O driver.
5+
* COPYRIGHT: Copyright 2019-2025 Dmitry Borisov ([email protected])
6+
*/
7+
8+
#pragma once
9+
10+
#define TAG_ATA_DEVICE 'dATA'
11+
12+
#define ATA_STATUS_SUCCESS 0
13+
#define ATA_STATUS_PENDING 1
14+
#define ATA_STATUS_ERROR 2
15+
#define ATA_STATUS_RESET 3
16+
#define ATA_STATUS_RETRY 4
17+
18+
#if defined(SARCH_PC98)
19+
/* Master/Slave devices for Bank 0 and Bank 1 */
20+
#define CHANNEL_MAX_DEVICES 4
21+
#define DEV_SLAVE(DeviceNumber) ((DeviceNumber) & 1)
22+
#else
23+
/* Master/Slave devices */
24+
#define CHANNEL_MAX_DEVICES 2
25+
#define DEV_SLAVE(DeviceNumber) (DeviceNumber)
26+
#endif
27+
28+
#if defined(SARCH_XBOX)
29+
/* It's safe to enable the multiple mode */
30+
#define ATA_ENABLE_MULTIPLE_MODE
31+
#endif
32+
33+
/* Delay of 400ns */
34+
#if defined(SARCH_PC98)
35+
#define ATA_IO_WAIT() WRITE_PORT_UCHAR((PUCHAR)0x5F, 0)
36+
#else
37+
#define ATA_IO_WAIT() StallExecutionProcessor(1)
38+
#endif
39+
40+
#if defined(_M_IX86) || defined(_M_AMD64)
41+
/* x86 I/O address space spans from 0 to 0xFFFF */
42+
typedef USHORT IDE_REG;
43+
#else
44+
typedef ULONG_PTR IDE_REG;
45+
#endif
46+
47+
#define ATA_MAX_LBA_28 0x0FFFFFFFULL
48+
#define ATA_MAX_LBA_48 (1ULL << 48)
49+
50+
#define IDE_FEATURE_PIO 0x00
51+
52+
#define IDE_DC_ALWAYS 0x08
53+
54+
#define IDE_DRIVE_SELECT 0xA0
55+
56+
#define ATAPI_INT_REASON_COD 0x01
57+
#define ATAPI_INT_REASON_IO 0x02
58+
#define ATAPI_INT_REASON_MASK (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD)
59+
60+
#define ATAPI_INT_REASON_STATUS_NEC 0x00
61+
#define ATAPI_INT_REASON_STATUS (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD)
62+
#define ATAPI_INT_REASON_AWAIT_CDB (IDE_STATUS_DRQ | ATAPI_INT_REASON_COD)
63+
#define ATAPI_INT_REASON_DATA_IN (ATAPI_INT_REASON_IO | IDE_STATUS_DRQ)
64+
65+
#define MAXIMUM_CDROM_SIZE 804 // == sizeof(CDROM_TOC)
66+
67+
#define ATA_TIME_BUSY_SELECT 2000 ///< 20 ms
68+
#define ATA_TIME_BUSY_POLL 500000 ///< 5 s
69+
#define ATA_TIME_BUSY_ENUM 100 ///< 1 ms
70+
#define ATA_TIME_BUSY_RESET 1000000 ///< 10 s
71+
#define ATA_TIME_RESET_SELECT 200000 ///< 2 s
72+
#define ATA_TIME_DRQ_CLEAR 100 ///< 200 us
73+
#define ATA_TIME_PHASE_CHANGE 100 ///< 1 ms
74+
75+
#define ATA_WRITE(Port, Value) \
76+
WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR)(Port), (Value))
77+
78+
#define ATA_WRITE_BLOCK_16(Port, Buffer, Count) \
79+
WRITE_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)(Port), (PUSHORT)(Buffer), (Count))
80+
81+
#define ATA_WRITE_BLOCK_32(Port, Buffer, Count) \
82+
WRITE_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)(Port), (PULONG)(Buffer), (Count))
83+
84+
#define ATA_READ(Port) \
85+
READ_PORT_UCHAR((PUCHAR)(ULONG_PTR)(Port))
86+
87+
#define ATA_READ_BLOCK_16(Port, Buffer, Count) \
88+
READ_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)(Port), (PUSHORT)(Buffer), (Count))
89+
90+
#define ATA_READ_BLOCK_32(Port, Buffer, Count) \
91+
READ_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)(Port), (PULONG)(Buffer), (Count))
92+
93+
typedef enum _ATA_DEVICE_CLASS
94+
{
95+
DEV_ATA,
96+
DEV_ATAPI,
97+
DEV_NONE,
98+
} ATA_DEVICE_CLASS, *PATA_DEVICE_CLASS;
99+
100+
typedef struct _IDE_REGISTERS
101+
{
102+
IDE_REG Data;
103+
union
104+
{
105+
IDE_REG Features;
106+
IDE_REG Error;
107+
};
108+
union
109+
{
110+
IDE_REG SectorCount;
111+
IDE_REG InterruptReason;
112+
};
113+
IDE_REG LbaLow; ///< LBA bits 0-7, 24-31
114+
union
115+
{
116+
IDE_REG LbaMid; ///< LBA bits 8-15, 32-39
117+
IDE_REG ByteCountLow;
118+
IDE_REG SignatureLow;
119+
};
120+
union
121+
{
122+
IDE_REG LbaHigh; ///< LBA bits 16-23, 40-47
123+
IDE_REG ByteCountHigh;
124+
IDE_REG SignatureHigh;
125+
};
126+
IDE_REG Device;
127+
union
128+
{
129+
IDE_REG Command;
130+
IDE_REG Status;
131+
};
132+
union
133+
{
134+
IDE_REG Control;
135+
IDE_REG AlternateStatus;
136+
};
137+
} IDE_REGISTERS, *PIDE_REGISTERS;
138+
139+
typedef struct _ATA_TASKFILE
140+
{
141+
UCHAR DriveSelect;
142+
UCHAR Command;
143+
struct
144+
{
145+
UCHAR Feature;
146+
UCHAR SectorCount;
147+
UCHAR LowLba;
148+
UCHAR MidLba;
149+
UCHAR HighLba;
150+
} Data[2]; // 0 - low part, 1 - high part
151+
} ATA_TASKFILE, *PATA_TASKFILE;
152+
153+
typedef struct _ATA_DEVICE_REQUEST
154+
{
155+
union
156+
{
157+
UCHAR Cdb[16];
158+
ATA_TASKFILE TaskFile;
159+
};
160+
PVOID DataBuffer;
161+
ULONG Flags;
162+
#define REQUEST_FLAG_LBA48 0x00000001
163+
#define REQUEST_FLAG_READ_WRITE_MULTIPLE 0x00000002
164+
#define REQUEST_FLAG_PACKET_COMMAND 0x00000004
165+
#define REQUEST_FLAG_SET_DEVICE_REGISTER 0x00000008
166+
#define REQUEST_FLAG_AWAIT_CDB 0x00000010
167+
#define REQUEST_FLAG_READ_COMMAND 0x00000020
168+
#define REQUEST_FLAG_IDENTIFY_COMMAND 0x00000040
169+
170+
ULONG DataTransferLength;
171+
} ATA_DEVICE_REQUEST, *PATA_DEVICE_REQUEST;
172+
173+
typedef struct _HW_DEVICE_UNIT
174+
{
175+
/* Public data, must be the first member */
176+
DEVICE_UNIT P;
177+
178+
IDE_REGISTERS Registers;
179+
ULONG BytesToTransfer;
180+
PUCHAR DataBuffer;
181+
ULONG DrqByteCount;
182+
ULONG MaximumTransferLength;
183+
UCHAR DeviceNumber;
184+
UCHAR DeviceSelect;
185+
UCHAR CdbSize;
186+
UCHAR MultiSectorTransfer;
187+
union
188+
{
189+
IDENTIFY_DEVICE_DATA IdentifyDeviceData;
190+
IDENTIFY_PACKET_DATA IdentifyPacketData;
191+
};
192+
} HW_DEVICE_UNIT, *PHW_DEVICE_UNIT;
193+
194+
FORCEINLINE
195+
BOOLEAN
196+
AtaDevIsIdentifyDataValid(
197+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
198+
{
199+
ULONG i;
200+
UCHAR Crc;
201+
202+
/* Bits 0:8 of word 255 */
203+
if (IdentifyData->Signature != 0xA5)
204+
{
205+
/* The integrity word is missing, assume the data provided by the device is valid */
206+
return TRUE;
207+
}
208+
209+
/* Verify the checksum */
210+
Crc = 0;
211+
for (i = 0; i < sizeof(*IdentifyData); ++i)
212+
{
213+
Crc += ((PUCHAR)IdentifyData)[i];
214+
}
215+
216+
return (Crc == 0);
217+
}
218+
219+
FORCEINLINE
220+
UCHAR
221+
AtaDevCdbSizeInWords(
222+
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
223+
{
224+
/* Bits 0:2 of word 0 */
225+
return (IdentifyPacketData->GeneralConfiguration.PacketType != 0) ? 8 : 6;
226+
}
227+
228+
FORCEINLINE
229+
BOOLEAN
230+
AtaDevHasLbaTranslation(
231+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
232+
{
233+
/* Bit 9 of word 49 */
234+
return IdentifyData->Capabilities.LbaSupported;
235+
}
236+
237+
FORCEINLINE
238+
ULONG
239+
AtaDevUserAddressableSectors28Bit(
240+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
241+
{
242+
/* Words 60-61 */
243+
return IdentifyData->UserAddressableSectors;
244+
}
245+
246+
FORCEINLINE
247+
ULONG64
248+
AtaDevUserAddressableSectors48Bit(
249+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
250+
{
251+
/* Words 100-103 */
252+
return ((ULONG64)IdentifyData->Max48BitLBA[1] << 32) | IdentifyData->Max48BitLBA[0];
253+
}
254+
255+
FORCEINLINE
256+
BOOLEAN
257+
AtaDevHas48BitAddressFeature(
258+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
259+
{
260+
/* Word 83: 15 = 0, 14 = 1 */
261+
if (IdentifyData->CommandSetSupport.WordValid83 == 1)
262+
{
263+
/* Bit 10 of word 83 */
264+
return IdentifyData->CommandSetSupport.BigLba;
265+
}
266+
267+
return FALSE;
268+
}
269+
270+
FORCEINLINE
271+
BOOLEAN
272+
AtaDevIsCurrentGeometryValid(
273+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
274+
{
275+
return ((IdentifyData->TranslationFieldsValid & 1) &&
276+
(IdentifyData->NumberOfCurrentCylinders != 0) &&
277+
(IdentifyData->NumberOfCurrentCylinders <= 63) &&
278+
(IdentifyData->NumberOfCurrentHeads != 0) &&
279+
(IdentifyData->NumberOfCurrentHeads <= 16) &&
280+
(IdentifyData->CurrentSectorsPerTrack != 0));
281+
}
282+
283+
FORCEINLINE
284+
VOID
285+
AtaDevDefaultChsTranslation(
286+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
287+
_Out_ PUSHORT Cylinders,
288+
_Out_ PUSHORT Heads,
289+
_Out_ PUSHORT SectorsPerTrack)
290+
{
291+
/* Word 1 */
292+
*Cylinders = IdentifyData->NumCylinders;
293+
294+
/* Word 3 */
295+
*Heads = IdentifyData->NumHeads;
296+
297+
/* Word 6 */
298+
*SectorsPerTrack = IdentifyData->NumSectorsPerTrack;
299+
}
300+
301+
FORCEINLINE
302+
VOID
303+
AtaDevCurrentChsTranslation(
304+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
305+
_Out_ PUSHORT Cylinders,
306+
_Out_ PUSHORT Heads,
307+
_Out_ PUSHORT SectorsPerTrack)
308+
{
309+
/* Word 54 */
310+
*Cylinders = IdentifyData->NumberOfCurrentCylinders;
311+
312+
/* Word 55 */
313+
*Heads = IdentifyData->NumberOfCurrentHeads;
314+
315+
/* Word 55 */
316+
*SectorsPerTrack = IdentifyData->CurrentSectorsPerTrack;
317+
}
318+
319+
FORCEINLINE
320+
UCHAR
321+
AtaDevCurrentSectorsPerDrq(
322+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
323+
{
324+
UCHAR MultiSectorCurrent;
325+
326+
/* Bit 8 of word 59 */
327+
if (!(IdentifyData->MultiSectorSettingValid))
328+
return 0;
329+
330+
/* The word 59 should be a power of 2 */
331+
MultiSectorCurrent = IdentifyData->CurrentMultiSectorSetting;
332+
333+
if ((MultiSectorCurrent > 0) && ((MultiSectorCurrent & (MultiSectorCurrent - 1)) == 0))
334+
return MultiSectorCurrent;
335+
336+
return 0;
337+
}
338+
339+
FORCEINLINE
340+
UCHAR
341+
AtaDevMaximumSectorsPerDrq(
342+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
343+
{
344+
UCHAR MultiSectorMax;
345+
346+
/* The word 47 should be a power of 2 */
347+
MultiSectorMax = IdentifyData->MaximumBlockTransfer;
348+
349+
if ((MultiSectorMax > 0) && ((MultiSectorMax & (MultiSectorMax - 1)) == 0))
350+
return MultiSectorMax;
351+
352+
return 0;
353+
}
354+
355+
FORCEINLINE
356+
ULONG
357+
AtaDevBytesPerLogicalSector(
358+
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
359+
{
360+
ULONG WordCount;
361+
362+
/* Word 106: 15 = 0, 14 = 1, 12 = 1 */
363+
if (IdentifyData->PhysicalLogicalSectorSize.Reserved1 == 1 &&
364+
IdentifyData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
365+
{
366+
/* Words 116-117 */
367+
WordCount = IdentifyData->WordsPerLogicalSector[0];
368+
WordCount |= (ULONG)IdentifyData->WordsPerLogicalSector[1] << 16;
369+
}
370+
else
371+
{
372+
/* 256 words = 512 bytes */
373+
WordCount = 256;
374+
}
375+
376+
return WordCount * sizeof(USHORT);
377+
}
378+
379+
FORCEINLINE
380+
BOOLEAN
381+
AtaCommandUseLba48(
382+
_In_ ULONG64 SectorNumber,
383+
_In_ ULONG SectorCount)
384+
{
385+
/* Use the 48-bit command when reasonable */
386+
return (((SectorNumber + SectorCount) >= ATA_MAX_LBA_28) || (SectorCount > 0x100));
387+
}

boot/freeldr/freeldr/arch/i386/pc98/machpc98.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ Pc98HwIdle(VOID)
3434
VOID
3535
Pc98PrepareForReactOS(VOID)
3636
{
37-
Pc98DiskPrepareForReactOS();
3837
Pc98VideoPrepareForReactOS();
3938
DiskStopFloppyMotor();
4039
DebugDisableScreenPort();

0 commit comments

Comments
 (0)