[FREELDR] Implement proper partition type detection and handling (#1762)
[reactos.git] / boot / freeldr / freeldr / arch / i386 / xboxdisk.c
1 /*
2 * FreeLoader
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Note: mostly ripped from atapi.c
19 * Some of this code was based on knowledge and/or code developed
20 * by the Xbox Linux group: http://www.xbox-linux.org
21 *
22 */
23
24 #include <freeldr.h>
25
26 #include <debug.h>
27
28 DBG_DEFAULT_CHANNEL(DISK);
29
30 #define XBOX_IDE_COMMAND_PORT 0x1f0
31 #define XBOX_IDE_CONTROL_PORT 0x170
32
33 #define IDE_SECTOR_BUF_SZ 512
34 #define IDE_MAX_POLL_RETRIES 100000
35 #define IDE_MAX_BUSY_RETRIES 50000
36
37 /* Control Block offsets and masks */
38 #define IDE_REG_ALT_STATUS 0x0000
39 #define IDE_REG_DEV_CNTRL 0x0000 /* device control register */
40 #define IDE_DC_SRST 0x04 /* drive reset (both drives) */
41 #define IDE_DC_nIEN 0x02 /* IRQ enable (active low) */
42 #define IDE_REG_DRV_ADDR 0x0001
43
44 /* Command Block offsets and masks */
45 #define IDE_REG_DATA_PORT 0x0000
46 #define IDE_REG_ERROR 0x0001 /* error register */
47 #define IDE_ER_AMNF 0x01 /* addr mark not found */
48 #define IDE_ER_TK0NF 0x02 /* track 0 not found */
49 #define IDE_ER_ABRT 0x04 /* command aborted */
50 #define IDE_ER_MCR 0x08 /* media change requested */
51 #define IDE_ER_IDNF 0x10 /* ID not found */
52 #define IDE_ER_MC 0x20 /* Media changed */
53 #define IDE_ER_UNC 0x40 /* Uncorrectable data error */
54 #define IDE_REG_PRECOMP 0x0001
55 #define IDE_REG_SECTOR_CNT 0x0002
56 #define IDE_REG_SECTOR_NUM 0x0003
57 #define IDE_REG_CYL_LOW 0x0004
58 #define IDE_REG_CYL_HIGH 0x0005
59 #define IDE_REG_DRV_HEAD 0x0006
60 #define IDE_DH_FIXED 0xA0
61 #define IDE_DH_LBA 0x40
62 #define IDE_DH_HDMASK 0x0F
63 #define IDE_DH_DRV0 0x00
64 #define IDE_DH_DRV1 0x10
65 #define IDE_REG_STATUS 0x0007
66 #define IDE_SR_BUSY 0x80
67 #define IDE_SR_DRDY 0x40
68 #define IDE_SR_WERR 0x20
69 #define IDE_SR_DRQ 0x08
70 #define IDE_SR_ERR 0x01
71 #define IDE_REG_COMMAND 0x0007
72
73 /* IDE/ATA commands */
74 #define IDE_CMD_RESET 0x08
75 #define IDE_CMD_READ 0x20
76 #define IDE_CMD_READ_RETRY 0x21
77 #define IDE_CMD_WRITE 0x30
78 #define IDE_CMD_WRITE_RETRY 0x31
79 #define IDE_CMD_PACKET 0xA0
80 #define IDE_CMD_READ_MULTIPLE 0xC4
81 #define IDE_CMD_WRITE_MULTIPLE 0xC5
82 #define IDE_CMD_READ_DMA 0xC8
83 #define IDE_CMD_WRITE_DMA 0xCA
84 #define IDE_CMD_FLUSH_CACHE 0xE7
85 #define IDE_CMD_FLUSH_CACHE_EXT 0xEA
86 #define IDE_CMD_IDENT_ATA_DRV 0xEC
87 #define IDE_CMD_IDENT_ATAPI_DRV 0xA1
88 #define IDE_CMD_GET_MEDIA_STATUS 0xDA
89
90 /*
91 * Access macros for command registers
92 * Each macro takes an address of the command port block, and data
93 */
94 #define IDEReadError(Address) \
95 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR)))
96 #define IDEWritePrecomp(Address, Data) \
97 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data)))
98 #define IDEReadSectorCount(Address) \
99 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT)))
100 #define IDEWriteSectorCount(Address, Data) \
101 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data)))
102 #define IDEReadSectorNum(Address) \
103 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM)))
104 #define IDEWriteSectorNum(Address, Data) \
105 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data)))
106 #define IDEReadCylinderLow(Address) \
107 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW)))
108 #define IDEWriteCylinderLow(Address, Data) \
109 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data)))
110 #define IDEReadCylinderHigh(Address) \
111 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH)))
112 #define IDEWriteCylinderHigh(Address, Data) \
113 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data)))
114 #define IDEReadDriveHead(Address) \
115 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD)))
116 #define IDEWriteDriveHead(Address, Data) \
117 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data)))
118 #define IDEReadStatus(Address) \
119 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS)))
120 #define IDEWriteCommand(Address, Data) \
121 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data)))
122 #define IDEReadDMACommand(Address) \
123 (READ_PORT_UCHAR((PUCHAR)((Address))))
124 #define IDEWriteDMACommand(Address, Data) \
125 (WRITE_PORT_UCHAR((PUCHAR)((Address)), (Data)))
126 #define IDEReadDMAStatus(Address) \
127 (READ_PORT_UCHAR((PUCHAR)((Address) + 2)))
128 #define IDEWriteDMAStatus(Address, Data) \
129 (WRITE_PORT_UCHAR((PUCHAR)((Address) + 2), (Data)))
130 #define IDEWritePRDTable(Address, Data) \
131 (WRITE_PORT_ULONG((PULONG)((Address) + 4), (Data)))
132
133 /*
134 * Data block read and write commands
135 */
136 #define IDEReadBlock(Address, Buffer, Count) \
137 (READ_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
138 #define IDEWriteBlock(Address, Buffer, Count) \
139 (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
140
141 #define IDEReadBlock32(Address, Buffer, Count) \
142 (READ_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
143 #define IDEWriteBlock32(Address, Buffer, Count) \
144 (WRITE_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
145
146 #define IDEReadWord(Address) \
147 (READ_PORT_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT)))
148
149 /*
150 * Access macros for control registers
151 * Each macro takes an address of the control port blank and data
152 */
153 #define IDEReadAltStatus(Address) \
154 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ALT_STATUS)))
155 #define IDEWriteDriveControl(Address, Data) \
156 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data)))
157
158 /* IDE_DRIVE_IDENTIFY */
159 typedef struct _IDE_DRIVE_IDENTIFY
160 {
161 USHORT ConfigBits; /*00*/
162 USHORT LogicalCyls; /*01*/
163 USHORT Reserved02; /*02*/
164 USHORT LogicalHeads; /*03*/
165 USHORT BytesPerTrack; /*04*/
166 USHORT BytesPerSector; /*05*/
167 USHORT SectorsPerTrack; /*06*/
168 UCHAR InterSectorGap; /*07*/
169 UCHAR InterSectorGapSize;
170 UCHAR Reserved08H; /*08*/
171 UCHAR BytesInPLO;
172 USHORT VendorUniqueCnt; /*09*/
173 UCHAR SerialNumber[20]; /*10*/
174 USHORT ControllerType; /*20*/
175 USHORT BufferSize; /*21*/
176 USHORT ECCByteCnt; /*22*/
177 UCHAR FirmwareRev[8]; /*23*/
178 UCHAR ModelNumber[40]; /*27*/
179 USHORT RWMultImplemented; /*47*/
180 USHORT DWordIo; /*48*/
181 USHORT Capabilities; /*49*/
182 #define IDE_DRID_STBY_SUPPORTED 0x2000
183 #define IDE_DRID_IORDY_SUPPORTED 0x0800
184 #define IDE_DRID_IORDY_DISABLE 0x0400
185 #define IDE_DRID_LBA_SUPPORTED 0x0200
186 #define IDE_DRID_DMA_SUPPORTED 0x0100
187 USHORT Reserved50; /*50*/
188 USHORT MinPIOTransTime; /*51*/
189 USHORT MinDMATransTime; /*52*/
190 USHORT TMFieldsValid; /*53*/
191 USHORT TMCylinders; /*54*/
192 USHORT TMHeads; /*55*/
193 USHORT TMSectorsPerTrk; /*56*/
194 USHORT TMCapacityLo; /*57*/
195 USHORT TMCapacityHi; /*58*/
196 USHORT RWMultCurrent; /*59*/
197 USHORT TMSectorCountLo; /*60*/
198 USHORT TMSectorCountHi; /*61*/
199 USHORT DmaModes; /*62*/
200 USHORT MultiDmaModes; /*63*/
201 USHORT Reserved64[5]; /*64*/
202 USHORT Reserved69[2]; /*69*/
203 USHORT Reserved71[4]; /*71*/
204 USHORT MaxQueueDepth; /*75*/
205 USHORT Reserved76[4]; /*76*/
206 USHORT MajorRevision; /*80*/
207 USHORT MinorRevision; /*81*/
208 USHORT SupportedFeatures82; /*82*/
209 USHORT SupportedFeatures83; /*83*/
210 USHORT SupportedFeatures84; /*84*/
211 USHORT EnabledFeatures85; /*85*/
212 USHORT EnabledFeatures86; /*86*/
213 USHORT EnabledFeatures87; /*87*/
214 USHORT UltraDmaModes; /*88*/
215 USHORT Reserved89[11]; /*89*/
216 USHORT Max48BitAddress[4]; /*100*/
217 USHORT Reserved104[151]; /*104*/
218 USHORT Checksum; /*255*/
219 } IDE_DRIVE_IDENTIFY, *PIDE_DRIVE_IDENTIFY;
220
221 /* XboxDiskPolledRead
222 *
223 * DESCRIPTION:
224 * Read a sector of data from the drive in a polled fashion.
225 *
226 * RUN LEVEL:
227 * PASSIVE_LEVEL
228 *
229 * ARGUMENTS:
230 * ULONG CommandPort Address of command port for drive
231 * ULONG ControlPort Address of control port for drive
232 * UCHAR PreComp Value to write to precomp register
233 * UCHAR SectorCnt Value to write to sectorCnt register
234 * UCHAR SectorNum Value to write to sectorNum register
235 * UCHAR CylinderLow Value to write to CylinderLow register
236 * UCHAR CylinderHigh Value to write to CylinderHigh register
237 * UCHAR DrvHead Value to write to Drive/Head register
238 * UCHAR Command Value to write to Command register
239 * PVOID Buffer Buffer for output data
240 *
241 * RETURNS:
242 * BOOLEAN: TRUE success, FALSE error
243 */
244 static BOOLEAN
245 XboxDiskPolledRead(ULONG CommandPort,
246 ULONG ControlPort,
247 UCHAR PreComp,
248 UCHAR SectorCnt,
249 UCHAR SectorNum,
250 UCHAR CylinderLow,
251 UCHAR CylinderHigh,
252 UCHAR DrvHead,
253 UCHAR Command,
254 PVOID Buffer)
255 {
256 ULONG SectorCount = 0;
257 ULONG RetryCount;
258 BOOLEAN Junk = FALSE;
259 UCHAR Status;
260
261 /* Wait for BUSY to clear */
262 for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
263 {
264 Status = IDEReadStatus(CommandPort);
265 if (!(Status & IDE_SR_BUSY))
266 break;
267
268 StallExecutionProcessor(10);
269 }
270 TRACE("status=0x%x\n", Status);
271 TRACE("waited %d usecs for busy to clear\n", RetryCount * 10);
272 if (RetryCount >= IDE_MAX_BUSY_RETRIES)
273 {
274 WARN("Drive is BUSY for too long\n");
275 return FALSE;
276 }
277
278 /* Write Drive/Head to select drive */
279 IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead);
280 StallExecutionProcessor(500);
281
282 /* Disable interrupts */
283 IDEWriteDriveControl(ControlPort, IDE_DC_nIEN);
284 StallExecutionProcessor(500);
285
286 /* Issue command to drive */
287 if (DrvHead & IDE_DH_LBA)
288 {
289 TRACE("READ:DRV=%d:LBA=1:BLK=%d:SC=0x%x:CM=0x%x\n",
290 DrvHead & IDE_DH_DRV1 ? 1 : 0,
291 ((DrvHead & 0x0f) << 24) + (CylinderHigh << 16) + (CylinderLow << 8) + SectorNum,
292 SectorCnt,
293 Command);
294 }
295 else
296 {
297 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",
298 DrvHead & IDE_DH_DRV1 ? 1 : 0,
299 CylinderHigh,
300 CylinderLow,
301 DrvHead & 0x0f,
302 SectorNum,
303 SectorCnt,
304 Command);
305 }
306
307 /* Setup command parameters */
308 IDEWritePrecomp(CommandPort, PreComp);
309 IDEWriteSectorCount(CommandPort, SectorCnt);
310 IDEWriteSectorNum(CommandPort, SectorNum);
311 IDEWriteCylinderHigh(CommandPort, CylinderHigh);
312 IDEWriteCylinderLow(CommandPort, CylinderLow);
313 IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead);
314
315 /* Issue the command */
316 IDEWriteCommand(CommandPort, Command);
317 StallExecutionProcessor(50);
318
319 /* Wait for DRQ or error */
320 for (RetryCount = 0; RetryCount < IDE_MAX_POLL_RETRIES; RetryCount++)
321 {
322 Status = IDEReadStatus(CommandPort);
323 if (!(Status & IDE_SR_BUSY))
324 {
325 if (Status & IDE_SR_ERR)
326 {
327 IDEWriteDriveControl(ControlPort, 0);
328 StallExecutionProcessor(50);
329 IDEReadStatus(CommandPort);
330 return FALSE;
331 }
332
333 if (Status & IDE_SR_DRQ)
334 {
335 break;
336 }
337 else
338 {
339 IDEWriteDriveControl(ControlPort, 0);
340 StallExecutionProcessor(50);
341 IDEReadStatus(CommandPort);
342 return FALSE;
343 }
344 }
345 StallExecutionProcessor(10);
346 }
347
348 /* Timed out */
349 if (RetryCount >= IDE_MAX_POLL_RETRIES)
350 {
351 IDEWriteDriveControl(ControlPort, 0);
352 StallExecutionProcessor(50);
353 IDEReadStatus(CommandPort);
354 return FALSE;
355 }
356
357 while (1)
358 {
359 /* Read data into buffer */
360 if (Junk == FALSE)
361 {
362 IDEReadBlock(CommandPort, Buffer, IDE_SECTOR_BUF_SZ);
363 Buffer = (PVOID)((ULONG_PTR)Buffer + IDE_SECTOR_BUF_SZ);
364 }
365 else
366 {
367 UCHAR JunkBuffer[IDE_SECTOR_BUF_SZ];
368 IDEReadBlock(CommandPort, JunkBuffer, IDE_SECTOR_BUF_SZ);
369 }
370 SectorCount++;
371
372 /* Check for error or more sectors to read */
373 for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
374 {
375 Status = IDEReadStatus(CommandPort);
376 if (!(Status & IDE_SR_BUSY))
377 {
378 if (Status & IDE_SR_ERR)
379 {
380 IDEWriteDriveControl(ControlPort, 0);
381 StallExecutionProcessor(50);
382 IDEReadStatus(CommandPort);
383 return FALSE;
384 }
385 if (Status & IDE_SR_DRQ)
386 {
387 if (SectorCount >= SectorCnt)
388 {
389 TRACE("Buffer size exceeded!\n");
390 Junk = TRUE;
391 }
392 break;
393 }
394 else
395 {
396 if (SectorCount > SectorCnt)
397 {
398 TRACE("Read %lu sectors of junk!\n",
399 SectorCount - SectorCnt);
400 }
401
402 IDEWriteDriveControl(ControlPort, 0);
403 StallExecutionProcessor(50);
404 IDEReadStatus(CommandPort);
405 return TRUE;
406 }
407 }
408 }
409 }
410 }
411
412 BOOLEAN
413 XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
414 {
415 ULONG StartSector;
416 UCHAR Count;
417
418 if (DriveNumber < 0x80 || (DriveNumber & 0x0f) >= 2)
419 {
420 /* Xbox has only 1 IDE controller and no floppy */
421 WARN("Invalid drive number\n");
422 return FALSE;
423 }
424
425 if (((SectorNumber + SectorCount) & UINT64_C(0xfffffffff0000000)) != UINT64_C(0))
426 {
427 FIXME("48bit LBA required but not implemented\n");
428 return FALSE;
429 }
430
431 StartSector = (ULONG) SectorNumber;
432 while (SectorCount > 0)
433 {
434 Count = (SectorCount <= 255 ? (UCHAR)SectorCount : 255);
435 if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT,
436 XBOX_IDE_CONTROL_PORT,
437 0, Count,
438 StartSector & 0xff,
439 (StartSector >> 8) & 0xff,
440 (StartSector >> 16) & 0xff,
441 ((StartSector >> 24) & 0x0f) | IDE_DH_LBA |
442 ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1),
443 IDE_CMD_READ,
444 Buffer))
445 {
446 return FALSE;
447 }
448 SectorCount -= Count;
449 Buffer = (PVOID) ((PCHAR) Buffer + Count * IDE_SECTOR_BUF_SZ);
450 }
451
452 return TRUE;
453 }
454
455 BOOLEAN
456 XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
457 {
458 IDE_DRIVE_IDENTIFY DrvParms;
459 ULONG i;
460 BOOLEAN Atapi;
461
462 Atapi = FALSE; /* FIXME */
463 /* Get the Drive Identify block from drive or die */
464 if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT,
465 XBOX_IDE_CONTROL_PORT,
466 0,
467 1,
468 0,
469 0,
470 0,
471 ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1),
472 (Atapi ? IDE_CMD_IDENT_ATAPI_DRV : IDE_CMD_IDENT_ATA_DRV),
473 (PUCHAR) &DrvParms))
474 {
475 ERR("XboxDiskPolledRead() failed\n");
476 return FALSE;
477 }
478
479 Geometry->Cylinders = DrvParms.LogicalCyls;
480 Geometry->Heads = DrvParms.LogicalHeads;
481 Geometry->Sectors = DrvParms.SectorsPerTrack;
482
483 if (!Atapi && (DrvParms.Capabilities & IDE_DRID_LBA_SUPPORTED) != 0)
484 {
485 /* LBA ATA drives always have a sector size of 512 */
486 Geometry->BytesPerSector = 512;
487 }
488 else
489 {
490 TRACE("BytesPerSector %d\n", DrvParms.BytesPerSector);
491 if (DrvParms.BytesPerSector == 0)
492 {
493 Geometry->BytesPerSector = 512;
494 }
495 else
496 {
497 for (i = 1 << 15; i; i /= 2)
498 {
499 if ((DrvParms.BytesPerSector & i) != 0)
500 {
501 Geometry->BytesPerSector = i;
502 break;
503 }
504 }
505 }
506 }
507 TRACE("Cylinders %d\n", Geometry->Cylinders);
508 TRACE("Heads %d\n", Geometry->Heads);
509 TRACE("Sectors %d\n", Geometry->Sectors);
510 TRACE("BytesPerSector %d\n", Geometry->BytesPerSector);
511
512 return TRUE;
513 }
514
515 ULONG
516 XboxDiskGetCacheableBlockCount(UCHAR DriveNumber)
517 {
518 /* 64 seems a nice number, it is used by the machpc code for LBA devices */
519 return 64;
520 }
521
522 /* EOF */