5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * Note: mostly ripped from atapi.c
20 * Some of this code was based on knowledge and/or code developed
21 * by the Xbox Linux group: http://www.xbox-linux.org
30 #define XBOX_IDE_COMMAND_PORT 0x1f0
31 #define XBOX_IDE_CONTROL_PORT 0x170
33 #define XBOX_SIGNATURE_SECTOR 3
34 #define XBOX_SIGNATURE ('B' | ('R' << 8) | ('F' << 16) | ('R' << 24))
38 ULONG SectorCountBeforePartition
;
39 ULONG PartitionSectorCount
;
40 UCHAR SystemIndicator
;
43 /* This is in the \Device\Harddisk0\Partition.. order used by the Xbox kernel */
44 { 0x0055F400, 0x0098f800, PARTITION_FAT32
}, /* Store, E: */
45 { 0x00465400, 0x000FA000, PARTITION_FAT_16
}, /* System, C: */
46 { 0x00000400, 0x00177000, PARTITION_FAT_16
}, /* Cache1, X: */
47 { 0x00177400, 0x00177000, PARTITION_FAT_16
}, /* Cache2, Y: */
48 { 0x002EE400, 0x00177000, PARTITION_FAT_16
} /* Cache3, Z: */
51 #define IDE_SECTOR_BUF_SZ 512
52 #define IDE_MAX_POLL_RETRIES 100000
53 #define IDE_MAX_BUSY_RETRIES 50000
55 /* Control Block offsets and masks */
56 #define IDE_REG_ALT_STATUS 0x0000
57 #define IDE_REG_DEV_CNTRL 0x0000 /* device control register */
58 #define IDE_DC_SRST 0x04 /* drive reset (both drives) */
59 #define IDE_DC_nIEN 0x02 /* IRQ enable (active low) */
60 #define IDE_REG_DRV_ADDR 0x0001
62 /* Command Block offsets and masks */
63 #define IDE_REG_DATA_PORT 0x0000
64 #define IDE_REG_ERROR 0x0001 /* error register */
65 #define IDE_ER_AMNF 0x01 /* addr mark not found */
66 #define IDE_ER_TK0NF 0x02 /* track 0 not found */
67 #define IDE_ER_ABRT 0x04 /* command aborted */
68 #define IDE_ER_MCR 0x08 /* media change requested */
69 #define IDE_ER_IDNF 0x10 /* ID not found */
70 #define IDE_ER_MC 0x20 /* Media changed */
71 #define IDE_ER_UNC 0x40 /* Uncorrectable data error */
72 #define IDE_REG_PRECOMP 0x0001
73 #define IDE_REG_SECTOR_CNT 0x0002
74 #define IDE_REG_SECTOR_NUM 0x0003
75 #define IDE_REG_CYL_LOW 0x0004
76 #define IDE_REG_CYL_HIGH 0x0005
77 #define IDE_REG_DRV_HEAD 0x0006
78 #define IDE_DH_FIXED 0xA0
79 #define IDE_DH_LBA 0x40
80 #define IDE_DH_HDMASK 0x0F
81 #define IDE_DH_DRV0 0x00
82 #define IDE_DH_DRV1 0x10
83 #define IDE_REG_STATUS 0x0007
84 #define IDE_SR_BUSY 0x80
85 #define IDE_SR_DRDY 0x40
86 #define IDE_SR_WERR 0x20
87 #define IDE_SR_DRQ 0x08
88 #define IDE_SR_ERR 0x01
89 #define IDE_REG_COMMAND 0x0007
91 /* IDE/ATA commands */
92 #define IDE_CMD_RESET 0x08
93 #define IDE_CMD_READ 0x20
94 #define IDE_CMD_READ_RETRY 0x21
95 #define IDE_CMD_WRITE 0x30
96 #define IDE_CMD_WRITE_RETRY 0x31
97 #define IDE_CMD_PACKET 0xA0
98 #define IDE_CMD_READ_MULTIPLE 0xC4
99 #define IDE_CMD_WRITE_MULTIPLE 0xC5
100 #define IDE_CMD_READ_DMA 0xC8
101 #define IDE_CMD_WRITE_DMA 0xCA
102 #define IDE_CMD_FLUSH_CACHE 0xE7
103 #define IDE_CMD_FLUSH_CACHE_EXT 0xEA
104 #define IDE_CMD_IDENT_ATA_DRV 0xEC
105 #define IDE_CMD_IDENT_ATAPI_DRV 0xA1
106 #define IDE_CMD_GET_MEDIA_STATUS 0xDA
109 * Access macros for command registers
110 * Each macro takes an address of the command port block, and data
112 #define IDEReadError(Address) \
113 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR)))
114 #define IDEWritePrecomp(Address, Data) \
115 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data)))
116 #define IDEReadSectorCount(Address) \
117 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT)))
118 #define IDEWriteSectorCount(Address, Data) \
119 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data)))
120 #define IDEReadSectorNum(Address) \
121 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM)))
122 #define IDEWriteSectorNum(Address, Data) \
123 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data)))
124 #define IDEReadCylinderLow(Address) \
125 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW)))
126 #define IDEWriteCylinderLow(Address, Data) \
127 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data)))
128 #define IDEReadCylinderHigh(Address) \
129 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH)))
130 #define IDEWriteCylinderHigh(Address, Data) \
131 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data)))
132 #define IDEReadDriveHead(Address) \
133 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD)))
134 #define IDEWriteDriveHead(Address, Data) \
135 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data)))
136 #define IDEReadStatus(Address) \
137 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS)))
138 #define IDEWriteCommand(Address, Data) \
139 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data)))
140 #define IDEReadDMACommand(Address) \
141 (READ_PORT_UCHAR((PUCHAR)((Address))))
142 #define IDEWriteDMACommand(Address, Data) \
143 (WRITE_PORT_UCHAR((PUCHAR)((Address)), (Data)))
144 #define IDEReadDMAStatus(Address) \
145 (READ_PORT_UCHAR((PUCHAR)((Address) + 2)))
146 #define IDEWriteDMAStatus(Address, Data) \
147 (WRITE_PORT_UCHAR((PUCHAR)((Address) + 2), (Data)))
148 #define IDEWritePRDTable(Address, Data) \
149 (WRITE_PORT_ULONG((PULONG)((Address) + 4), (Data)))
152 * Data block read and write commands
154 #define IDEReadBlock(Address, Buffer, Count) \
155 (__inwordstring(((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
156 #define IDEWriteBlock(Address, Buffer, Count) \
157 (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
159 #define IDEReadBlock32(Address, Buffer, Count) \
160 (READ_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
161 #define IDEWriteBlock32(Address, Buffer, Count) \
162 (WRITE_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
164 #define IDEReadWord(Address) \
165 (READ_PORT_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT)))
168 * Access macros for control registers
169 * Each macro takes an address of the control port blank and data
171 #define IDEReadAltStatus(Address) \
172 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ALT_STATUS)))
173 #define IDEWriteDriveControl(Address, Data) \
174 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data)))
176 /* IDE_DRIVE_IDENTIFY */
178 typedef struct _IDE_DRIVE_IDENTIFY
180 USHORT ConfigBits
; /*00*/
181 USHORT LogicalCyls
; /*01*/
182 USHORT Reserved02
; /*02*/
183 USHORT LogicalHeads
; /*03*/
184 USHORT BytesPerTrack
; /*04*/
185 USHORT BytesPerSector
; /*05*/
186 USHORT SectorsPerTrack
; /*06*/
187 UCHAR InterSectorGap
; /*07*/
188 UCHAR InterSectorGapSize
;
189 UCHAR Reserved08H
; /*08*/
191 USHORT VendorUniqueCnt
; /*09*/
192 char SerialNumber
[20]; /*10*/
193 USHORT ControllerType
; /*20*/
194 USHORT BufferSize
; /*21*/
195 USHORT ECCByteCnt
; /*22*/
196 char FirmwareRev
[8]; /*23*/
197 char ModelNumber
[40]; /*27*/
198 USHORT RWMultImplemented
; /*47*/
199 USHORT DWordIo
; /*48*/
200 USHORT Capabilities
; /*49*/
201 #define IDE_DRID_STBY_SUPPORTED 0x2000
202 #define IDE_DRID_IORDY_SUPPORTED 0x0800
203 #define IDE_DRID_IORDY_DISABLE 0x0400
204 #define IDE_DRID_LBA_SUPPORTED 0x0200
205 #define IDE_DRID_DMA_SUPPORTED 0x0100
206 USHORT Reserved50
; /*50*/
207 USHORT MinPIOTransTime
; /*51*/
208 USHORT MinDMATransTime
; /*52*/
209 USHORT TMFieldsValid
; /*53*/
210 USHORT TMCylinders
; /*54*/
211 USHORT TMHeads
; /*55*/
212 USHORT TMSectorsPerTrk
; /*56*/
213 USHORT TMCapacityLo
; /*57*/
214 USHORT TMCapacityHi
; /*58*/
215 USHORT RWMultCurrent
; /*59*/
216 USHORT TMSectorCountLo
; /*60*/
217 USHORT TMSectorCountHi
; /*61*/
218 USHORT DmaModes
; /*62*/
219 USHORT MultiDmaModes
; /*63*/
220 USHORT Reserved64
[5]; /*64*/
221 USHORT Reserved69
[2]; /*69*/
222 USHORT Reserved71
[4]; /*71*/
223 USHORT MaxQueueDepth
; /*75*/
224 USHORT Reserved76
[4]; /*76*/
225 USHORT MajorRevision
; /*80*/
226 USHORT MinorRevision
; /*81*/
227 USHORT SupportedFeatures82
; /*82*/
228 USHORT SupportedFeatures83
; /*83*/
229 USHORT SupportedFeatures84
; /*84*/
230 USHORT EnabledFeatures85
; /*85*/
231 USHORT EnabledFeatures86
; /*86*/
232 USHORT EnabledFeatures87
; /*87*/
233 USHORT UltraDmaModes
; /*88*/
234 USHORT Reserved89
[11]; /*89*/
235 USHORT Max48BitAddress
[4]; /*100*/
236 USHORT Reserved104
[151]; /*104*/
237 USHORT Checksum
; /*255*/
238 } IDE_DRIVE_IDENTIFY
, *PIDE_DRIVE_IDENTIFY
;
240 /* XboxDiskPolledRead
243 * Read a sector of data from the drive in a polled fashion.
249 * ULONG CommandPort Address of command port for drive
250 * ULONG ControlPort Address of control port for drive
251 * UCHAR PreComp Value to write to precomp register
252 * UCHAR SectorCnt Value to write to sectorCnt register
253 * UCHAR SectorNum Value to write to sectorNum register
254 * UCHAR CylinderLow Value to write to CylinderLow register
255 * UCHAR CylinderHigh Value to write to CylinderHigh register
256 * UCHAR DrvHead Value to write to Drive/Head register
257 * UCHAR Command Value to write to Command register
258 * PVOID Buffer Buffer for output data
261 * BOOLEAN: TRUE success, FALSE error
265 XboxDiskPolledRead(ULONG CommandPort
,
276 ULONG SectorCount
= 0;
278 BOOLEAN Junk
= FALSE
;
281 /* Wait for BUSY to clear */
282 for (RetryCount
= 0; RetryCount
< IDE_MAX_BUSY_RETRIES
; RetryCount
++)
284 Status
= IDEReadStatus(CommandPort
);
285 if (!(Status
& IDE_SR_BUSY
))
289 StallExecutionProcessor(10);
291 DbgPrint((DPRINT_DISK
, "status=0x%x\n", Status
));
292 DbgPrint((DPRINT_DISK
, "waited %d usecs for busy to clear\n", RetryCount
* 10));
293 if (RetryCount
>= IDE_MAX_BUSY_RETRIES
)
295 DbgPrint((DPRINT_DISK
, "Drive is BUSY for too long\n"));
299 /* Write Drive/Head to select drive */
300 IDEWriteDriveHead(CommandPort
, IDE_DH_FIXED
| DrvHead
);
301 StallExecutionProcessor(500);
303 /* Disable interrupts */
304 IDEWriteDriveControl(ControlPort
, IDE_DC_nIEN
);
305 StallExecutionProcessor(500);
307 /* Issue command to drive */
308 if (DrvHead
& IDE_DH_LBA
)
310 DbgPrint((DPRINT_DISK
, "READ:DRV=%d:LBA=1:BLK=%d:SC=0x%x:CM=0x%x\n",
311 DrvHead
& IDE_DH_DRV1
? 1 : 0,
312 ((DrvHead
& 0x0f) << 24) + (CylinderHigh
<< 16) + (CylinderLow
<< 8) + SectorNum
,
318 DbgPrint((DPRINT_DISK
, "READ:DRV=%d:LBA=0:CH=0x%x:CL=0x%x:HD=0x%x:SN=0x%x:SC=0x%x:CM=0x%x\n",
319 DrvHead
& IDE_DH_DRV1
? 1 : 0,
328 /* Setup command parameters */
329 IDEWritePrecomp(CommandPort
, PreComp
);
330 IDEWriteSectorCount(CommandPort
, SectorCnt
);
331 IDEWriteSectorNum(CommandPort
, SectorNum
);
332 IDEWriteCylinderHigh(CommandPort
, CylinderHigh
);
333 IDEWriteCylinderLow(CommandPort
, CylinderLow
);
334 IDEWriteDriveHead(CommandPort
, IDE_DH_FIXED
| DrvHead
);
336 /* Issue the command */
337 IDEWriteCommand(CommandPort
, Command
);
338 StallExecutionProcessor(50);
340 /* wait for DRQ or error */
341 for (RetryCount
= 0; RetryCount
< IDE_MAX_POLL_RETRIES
; RetryCount
++)
343 Status
= IDEReadStatus(CommandPort
);
344 if (!(Status
& IDE_SR_BUSY
))
346 if (Status
& IDE_SR_ERR
)
348 IDEWriteDriveControl(ControlPort
, 0);
349 StallExecutionProcessor(50);
350 IDEReadStatus(CommandPort
);
355 if (Status
& IDE_SR_DRQ
)
361 IDEWriteDriveControl(ControlPort
, 0);
362 StallExecutionProcessor(50);
363 IDEReadStatus(CommandPort
);
368 StallExecutionProcessor(10);
372 if (RetryCount
>= IDE_MAX_POLL_RETRIES
)
374 IDEWriteDriveControl(ControlPort
, 0);
375 StallExecutionProcessor(50);
376 IDEReadStatus(CommandPort
);
383 /* Read data into buffer */
386 IDEReadBlock(CommandPort
, Buffer
, IDE_SECTOR_BUF_SZ
);
387 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ IDE_SECTOR_BUF_SZ
);
391 UCHAR JunkBuffer
[IDE_SECTOR_BUF_SZ
];
392 IDEReadBlock(CommandPort
, JunkBuffer
, IDE_SECTOR_BUF_SZ
);
396 /* Check for error or more sectors to read */
397 for (RetryCount
= 0; RetryCount
< IDE_MAX_BUSY_RETRIES
; RetryCount
++)
399 Status
= IDEReadStatus(CommandPort
);
400 if (!(Status
& IDE_SR_BUSY
))
402 if (Status
& IDE_SR_ERR
)
404 IDEWriteDriveControl(ControlPort
, 0);
405 StallExecutionProcessor(50);
406 IDEReadStatus(CommandPort
);
410 if (Status
& IDE_SR_DRQ
)
412 if (SectorCount
>= SectorCnt
)
414 DbgPrint((DPRINT_DISK
, "Buffer size exceeded!\n"));
421 if (SectorCount
> SectorCnt
)
423 DbgPrint((DPRINT_DISK
, "Read %lu sectors of junk!\n",
424 SectorCount
- SectorCnt
));
426 IDEWriteDriveControl(ControlPort
, 0);
427 StallExecutionProcessor(50);
428 IDEReadStatus(CommandPort
);
438 XboxDiskReadLogicalSectors(ULONG DriveNumber
, ULONGLONG SectorNumber
, ULONG SectorCount
, PVOID Buffer
)
443 if (DriveNumber
< 0x80 || 2 <= (DriveNumber
& 0x0f))
445 /* Xbox has only 1 IDE controller and no floppy */
446 DbgPrint((DPRINT_DISK
, "Invalid drive number\n"));
450 if (UINT64_C(0) != ((SectorNumber
+ SectorCount
) & UINT64_C(0xfffffffff0000000)))
452 DbgPrint((DPRINT_DISK
, "48bit LBA required but not implemented\n"));
456 StartSector
= (ULONG
) SectorNumber
;
457 while (0 < SectorCount
)
459 Count
= (SectorCount
<= 255 ? SectorCount
: 255);
460 if (! XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT
,
461 XBOX_IDE_CONTROL_PORT
,
464 (StartSector
>> 8) & 0xff,
465 (StartSector
>> 16) & 0xff,
466 ((StartSector
>> 24) & 0x0f) | IDE_DH_LBA
|
467 (0 == (DriveNumber
& 0x0f) ? IDE_DH_DRV0
: IDE_DH_DRV1
),
473 SectorCount
-= Count
;
474 Buffer
= (PVOID
) ((PCHAR
) Buffer
+ Count
* IDE_SECTOR_BUF_SZ
);
481 XboxDiskGetPartitionEntry(ULONG DriveNumber
, ULONG PartitionNumber
, PPARTITION_TABLE_ENTRY PartitionTableEntry
)
483 UCHAR SectorData
[IDE_SECTOR_BUF_SZ
];
485 /* This is the Xbox, chances are that there is a Xbox-standard partitionless
486 * disk in it so let's check that first */
488 if (1 <= PartitionNumber
&& PartitionNumber
<= sizeof(XboxPartitions
) / sizeof(XboxPartitions
[0]) &&
489 MachDiskReadLogicalSectors(DriveNumber
, XBOX_SIGNATURE_SECTOR
, 1, SectorData
))
491 if (*((PULONG
) SectorData
) == XBOX_SIGNATURE
)
493 memset(PartitionTableEntry
, 0, sizeof(PARTITION_TABLE_ENTRY
));
494 PartitionTableEntry
->SystemIndicator
= XboxPartitions
[PartitionNumber
- 1].SystemIndicator
;
495 PartitionTableEntry
->SectorCountBeforePartition
= XboxPartitions
[PartitionNumber
- 1].SectorCountBeforePartition
;
496 PartitionTableEntry
->PartitionSectorCount
= XboxPartitions
[PartitionNumber
- 1].PartitionSectorCount
;
501 /* No magic Xbox partitions. Maybe there's a MBR */
502 return DiskGetPartitionEntry(DriveNumber
, PartitionNumber
, PartitionTableEntry
);
506 XboxDiskGetDriveGeometry(ULONG DriveNumber
, PGEOMETRY Geometry
)
508 IDE_DRIVE_IDENTIFY DrvParms
;
512 Atapi
= FALSE
; /* FIXME */
513 /* Get the Drive Identify block from drive or die */
514 if (! XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT
,
515 XBOX_IDE_CONTROL_PORT
,
521 (0 == (DriveNumber
& 0x0f) ? IDE_DH_DRV0
: IDE_DH_DRV1
),
522 (Atapi
? IDE_CMD_IDENT_ATAPI_DRV
: IDE_CMD_IDENT_ATA_DRV
),
525 DbgPrint((DPRINT_DISK
, "XboxDiskPolledRead() failed\n"));
529 Geometry
->Cylinders
= DrvParms
.LogicalCyls
;
530 Geometry
->Heads
= DrvParms
.LogicalHeads
;
531 Geometry
->Sectors
= DrvParms
.SectorsPerTrack
;
533 if (! Atapi
&& 0 != (DrvParms
.Capabilities
& IDE_DRID_LBA_SUPPORTED
))
535 /* LBA ATA drives always have a sector size of 512 */
536 Geometry
->BytesPerSector
= 512;
540 DbgPrint((DPRINT_DISK
, "BytesPerSector %d\n", DrvParms
.BytesPerSector
));
541 if (DrvParms
.BytesPerSector
== 0)
543 Geometry
->BytesPerSector
= 512;
547 for (i
= 15; i
>= 0; i
--)
549 if (0 != (DrvParms
.BytesPerSector
& (1 << i
)))
551 Geometry
->BytesPerSector
= 1 << i
;
557 DbgPrint((DPRINT_DISK
, "Cylinders %d\n", Geometry
->Cylinders
));
558 DbgPrint((DPRINT_DISK
, "Heads %d\n", Geometry
->Heads
));
559 DbgPrint((DPRINT_DISK
, "Sectors %d\n", Geometry
->Sectors
));
560 DbgPrint((DPRINT_DISK
, "BytesPerSector %d\n", Geometry
->BytesPerSector
));
566 XboxDiskGetCacheableBlockCount(ULONG DriveNumber
)
568 /* 64 seems a nice number, it is used by the machpc code for LBA devices */