Replace BOOL by BOOLEAN and STDCALL by NTAPI
[reactos.git] / reactos / boot / freeldr / freeldr / arch / i386 / i386disk.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 #include <freeldr.h>
21
22 #define NDEBUG
23 #include <debug.h>
24
25 /////////////////////////////////////////////////////////////////////////////////////////////
26 // FUNCTIONS
27 /////////////////////////////////////////////////////////////////////////////////////////////
28
29 #ifdef __i386__
30
31 BOOLEAN DiskResetController(ULONG DriveNumber)
32 {
33 REGS RegsIn;
34 REGS RegsOut;
35
36 DbgPrint((DPRINT_DISK, "DiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING CONTROLLER\n", DriveNumber));
37
38 // BIOS Int 13h, function 0 - Reset disk system
39 // AH = 00h
40 // DL = drive (if bit 7 is set both hard disks and floppy disks reset)
41 // Return:
42 // AH = status
43 // CF clear if successful
44 // CF set on error
45 RegsIn.b.ah = 0x00;
46 RegsIn.b.dl = DriveNumber;
47
48 // Reset the disk controller
49 Int386(0x13, &RegsIn, &RegsOut);
50
51 return INT386_SUCCESS(RegsOut);
52 }
53
54 BOOLEAN DiskInt13ExtensionsSupported(ULONG DriveNumber)
55 {
56 REGS RegsIn;
57 REGS RegsOut;
58
59 DbgPrint((DPRINT_DISK, "DiskInt13ExtensionsSupported()\n"));
60
61 // IBM/MS INT 13 Extensions - INSTALLATION CHECK
62 // AH = 41h
63 // BX = 55AAh
64 // DL = drive (80h-FFh)
65 // Return:
66 // CF set on error (extensions not supported)
67 // AH = 01h (invalid function)
68 // CF clear if successful
69 // BX = AA55h if installed
70 // AH = major version of extensions
71 // 01h = 1.x
72 // 20h = 2.0 / EDD-1.0
73 // 21h = 2.1 / EDD-1.1
74 // 30h = EDD-3.0
75 // AL = internal use
76 // CX = API subset support bitmap
77 // DH = extension version (v2.0+ ??? -- not present in 1.x)
78 //
79 // Bitfields for IBM/MS INT 13 Extensions API support bitmap
80 // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
81 // Bit 1, removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
82 // Bit 2, enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
83 // extended drive parameter table is valid
84 // Bits 3-15 reserved
85 RegsIn.b.ah = 0x41;
86 RegsIn.w.bx = 0x55AA;
87 RegsIn.b.dl = DriveNumber;
88
89 // Reset the disk controller
90 Int386(0x13, &RegsIn, &RegsOut);
91
92 if (!INT386_SUCCESS(RegsOut))
93 {
94 // CF set on error (extensions not supported)
95 return FALSE;
96 }
97
98 if (RegsOut.w.bx != 0xAA55)
99 {
100 // BX = AA55h if installed
101 return FALSE;
102 }
103
104 // Note:
105 // The original check is too strict because some BIOSes report that
106 // extended disk access functions are not suported when booting
107 // from a CD (e.g. Phoenix BIOS v6.00PG). Argh!
108 #if 0
109 if (!(RegsOut.w.cx & 0x0001))
110 {
111 // CX = API subset support bitmap
112 // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
113 return FALSE;
114 }
115 #endif
116
117 // Use this relaxed check instead
118 if (RegsOut.w.cx == 0x0000)
119 {
120 // CX = API subset support bitmap
121 return FALSE;
122 }
123
124 return TRUE;
125 }
126
127 VOID DiskStopFloppyMotor(VOID)
128 {
129 WRITE_PORT_UCHAR((PUCHAR)0x3F2, 0);
130 }
131
132 BOOLEAN DiskGetExtendedDriveParameters(ULONG DriveNumber, PVOID Buffer, USHORT BufferSize)
133 {
134 REGS RegsIn;
135 REGS RegsOut;
136 PUSHORT Ptr = (PUSHORT)(BIOSCALLBUFFER);
137
138 DbgPrint((DPRINT_DISK, "DiskGetExtendedDriveParameters()\n"));
139
140 // Initialize transfer buffer
141 *Ptr = BufferSize;
142
143 // BIOS Int 13h, function 48h - Get drive parameters
144 // AH = 48h
145 // DL = drive (bit 7 set for hard disk)
146 // DS:SI = result buffer
147 // Return:
148 // CF set on error
149 // AH = status (07h)
150 // CF clear if successful
151 // AH = 00h
152 // DS:SI -> result buffer
153 RegsIn.b.ah = 0x48;
154 RegsIn.b.dl = DriveNumber;
155 RegsIn.x.ds = BIOSCALLBUFSEGMENT; // DS:SI -> result buffer
156 RegsIn.w.si = BIOSCALLBUFOFFSET;
157
158 // Get drive parameters
159 Int386(0x13, &RegsIn, &RegsOut);
160
161 if (!INT386_SUCCESS(RegsOut))
162 {
163 return FALSE;
164 }
165
166 memcpy(Buffer, Ptr, BufferSize);
167
168 #ifdef DEBUG
169 DbgPrint((DPRINT_DISK, "size of buffer: %x\n", Ptr[0]));
170 DbgPrint((DPRINT_DISK, "information flags: %x\n", Ptr[1]));
171 DbgPrint((DPRINT_DISK, "number of physical cylinders on drive: %u\n", *(PULONG)&Ptr[2]));
172 DbgPrint((DPRINT_DISK, "number of physical heads on drive: %u\n", *(PULONG)&Ptr[4]));
173 DbgPrint((DPRINT_DISK, "number of physical sectors per track: %u\n", *(PULONG)&Ptr[6]));
174 DbgPrint((DPRINT_DISK, "total number of sectors on drive: %I64u\n", *(unsigned long long*)&Ptr[8]));
175 DbgPrint((DPRINT_DISK, "bytes per sector: %u\n", Ptr[12]));
176 if (Ptr[0] >= 0x1e)
177 {
178 DbgPrint((DPRINT_DISK, "EED configuration parameters: %x:%x\n", Ptr[13], Ptr[14]));
179 if (Ptr[13] != 0xffff && Ptr[14] != 0xffff)
180 {
181 PUCHAR SpecPtr = (PUCHAR)((Ptr[13] << 4) + Ptr[14]);
182 DbgPrint((DPRINT_DISK, "SpecPtr: %x\n", SpecPtr));
183 DbgPrint((DPRINT_DISK, "physical I/O port base address: %x\n", *(PUSHORT)&SpecPtr[0]));
184 DbgPrint((DPRINT_DISK, "disk-drive control port address: %x\n", *(PUSHORT)&SpecPtr[2]));
185 DbgPrint((DPRINT_DISK, "drive flags: %x\n", SpecPtr[4]));
186 DbgPrint((DPRINT_DISK, "proprietary information: %x\n", SpecPtr[5]));
187 DbgPrint((DPRINT_DISK, "IRQ for drive: %u\n", SpecPtr[6]));
188 DbgPrint((DPRINT_DISK, "sector count for multi-sector transfers: %u\n", SpecPtr[7]));
189 DbgPrint((DPRINT_DISK, "DMA control: %x\n", SpecPtr[8]));
190 DbgPrint((DPRINT_DISK, "programmed I/O control: %x\n", SpecPtr[9]));
191 DbgPrint((DPRINT_DISK, "drive options: %x\n", *(PUSHORT)&SpecPtr[10]));
192 }
193 }
194 if (Ptr[0] >= 0x42)
195 {
196 DbgPrint((DPRINT_DISK, "signature: %x\n", Ptr[15]));
197 }
198 #endif
199
200 return TRUE;
201 }
202
203 BOOLEAN i386DiskGetBootVolume(PULONG DriveNumber, PULONGLONG StartSector, PULONGLONG SectorCount, int *FsType)
204 {
205 PARTITION_TABLE_ENTRY PartitionTableEntry;
206 UCHAR VolumeType;
207 ULONG ActivePartition;
208
209 DbgPrint((DPRINT_FILESYSTEM, "FsOpenVolume() DriveNumber: 0x%x PartitionNumber: 0x%x\n", i386BootDrive, i386BootPartition));
210
211 // Check and see if it is a floppy drive
212 // If so then just assume FAT12 file system type
213 if (DiskIsDriveRemovable(i386BootDrive))
214 {
215 DbgPrint((DPRINT_FILESYSTEM, "Drive is a floppy diskette drive. Assuming FAT12 file system.\n"));
216
217 *DriveNumber = i386BootDrive;
218 *StartSector = 0;
219 *SectorCount = 2 * 80 * 18; /* FIXME hardcoded for 1.44 Mb */
220 *FsType = FS_FAT;
221 return TRUE;
222 }
223
224 // Check for ISO9660 file system type
225 if (i386BootDrive >= 0x80 && FsRecIsIso9660(i386BootDrive))
226 {
227 DbgPrint((DPRINT_FILESYSTEM, "Drive is a cdrom drive. Assuming ISO-9660 file system.\n"));
228
229 *DriveNumber = i386BootDrive;
230 *StartSector = 0;
231 *SectorCount = 0;
232 *FsType = FS_ISO9660;
233 return TRUE;
234 }
235
236 // Get the requested partition entry
237 if (i386BootPartition == 0)
238 {
239 // Partition requested was zero which means the boot partition
240 if (! DiskGetActivePartitionEntry(i386BootDrive, &PartitionTableEntry, &ActivePartition))
241 {
242 /* Try partition-less disk */
243 *StartSector = 0;
244 *SectorCount = 0;
245 }
246 /* Check for valid partition */
247 else if (PartitionTableEntry.SystemIndicator == PARTITION_ENTRY_UNUSED)
248 {
249 return FALSE;
250 }
251 else
252 {
253 *StartSector = PartitionTableEntry.SectorCountBeforePartition;
254 *SectorCount = PartitionTableEntry.PartitionSectorCount;
255 }
256 }
257 else if (0xff == i386BootPartition)
258 {
259 /* Partition-less disk */
260 *StartSector = 0;
261 *SectorCount = 0;
262 }
263 else
264 {
265 // Get requested partition
266 if (! MachDiskGetPartitionEntry(i386BootDrive, i386BootPartition, &PartitionTableEntry))
267 {
268 return FALSE;
269 }
270 /* Check for valid partition */
271 else if (PartitionTableEntry.SystemIndicator == PARTITION_ENTRY_UNUSED)
272 {
273 return FALSE;
274 }
275 else
276 {
277 *StartSector = PartitionTableEntry.SectorCountBeforePartition;
278 *SectorCount = PartitionTableEntry.PartitionSectorCount;
279 }
280 }
281
282 // Try to recognize the file system
283 if (!FsRecognizeVolume(i386BootDrive, *StartSector, &VolumeType))
284 {
285 return FALSE;
286 }
287
288 *DriveNumber = i386BootDrive;
289
290 switch (VolumeType)
291 {
292 case PARTITION_FAT_12:
293 case PARTITION_FAT_16:
294 case PARTITION_HUGE:
295 case PARTITION_XINT13:
296 case PARTITION_FAT32:
297 case PARTITION_FAT32_XINT13:
298 *FsType = FS_FAT;
299 return TRUE;
300 case PARTITION_EXT2:
301 *FsType = FS_EXT2;
302 return TRUE;
303 case PARTITION_NTFS:
304 *FsType = FS_NTFS;
305 return TRUE;
306 default:
307 *FsType = 0;
308 return FALSE;
309 }
310
311 return TRUE;
312 }
313
314 VOID
315 i386DiskGetBootDevice(PULONG BootDevice)
316 {
317 ((char *)BootDevice)[0] = (char)i386BootDrive;
318 ((char *)BootDevice)[1] = (char)i386BootPartition;
319 }
320
321 BOOLEAN
322 i386DiskBootingFromFloppy(VOID)
323 {
324 return i386BootDrive < 0x80;
325 }
326
327 #define IsRecognizedPartition(P) \
328 ((P) == PARTITION_FAT_12 || \
329 (P) == PARTITION_FAT_16 || \
330 (P) == PARTITION_HUGE || \
331 (P) == PARTITION_IFS || \
332 (P) == PARTITION_EXT2 || \
333 (P) == PARTITION_FAT32 || \
334 (P) == PARTITION_FAT32_XINT13 || \
335 (P) == PARTITION_XINT13)
336
337 #define IsContainerPartition(P) \
338 ((P) == PARTITION_EXTENDED || \
339 (P) == PARTITION_XINT13_EXTENDED)
340
341 BOOLEAN i386DiskGetSystemVolume(char *SystemPath,
342 char *RemainingPath,
343 PULONG Device,
344 PULONG DriveNumber,
345 PULONGLONG StartSector,
346 PULONGLONG SectorCount,
347 int *FsType)
348 {
349 ULONG PartitionNumber;
350 PARTITION_TABLE_ENTRY PartitionTableEntry;
351 UCHAR VolumeType;
352 CHAR BootPath[256];
353 unsigned i, RosPartition;
354
355 /*
356 * Verify system path
357 */
358 if (!DissectArcPath(SystemPath, BootPath, DriveNumber, &PartitionNumber))
359 {
360 return FALSE;
361 }
362 if (NULL != RemainingPath)
363 {
364 strcpy(RemainingPath, BootPath);
365 }
366
367 /* 0xff -> no partition table present, use whole device */
368 if (0xff == PartitionNumber)
369 {
370 PartitionTableEntry.SectorCountBeforePartition = 0;
371 i = 0xff;
372 }
373 else
374 {
375 /* recalculate the boot partition for freeldr */
376 i = 0;
377 RosPartition = 0;
378 while (1)
379 {
380 if (!MachDiskGetPartitionEntry(*DriveNumber, ++i, &PartitionTableEntry))
381 {
382 return FALSE;
383 }
384 if (!IsContainerPartition(PartitionTableEntry.SystemIndicator) &&
385 PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
386 {
387 if (++RosPartition == PartitionNumber)
388 {
389 break;
390 }
391 }
392 }
393 }
394
395 /* Check for ISO9660 file system type */
396 if (*DriveNumber >= 0x80 && FsRecIsIso9660(*DriveNumber))
397 {
398 DbgPrint((DPRINT_FILESYSTEM, "Drive is a cdrom drive. Assuming ISO-9660 file system.\n"));
399
400 if (NULL != Device)
401 {
402 ((char *)Device)[0] = (char)(*DriveNumber);
403 ((char *)Device)[1] = (char)i;
404 }
405 *StartSector = 0;
406 *SectorCount = 0;
407 *FsType = FS_ISO9660;
408 return TRUE;
409 }
410
411 if (!FsRecognizeVolume(*DriveNumber, PartitionTableEntry.SectorCountBeforePartition, &VolumeType))
412 {
413 return FALSE;
414 }
415
416 if (NULL != Device)
417 {
418 ((char *)Device)[0] = (char)(*DriveNumber);
419 ((char *)Device)[1] = (char)i;
420 }
421 *StartSector = PartitionTableEntry.SectorCountBeforePartition;
422 *SectorCount = PartitionTableEntry.PartitionSectorCount;
423
424 switch (VolumeType)
425 {
426 case PARTITION_FAT_12:
427 case PARTITION_FAT_16:
428 case PARTITION_HUGE:
429 case PARTITION_XINT13:
430 case PARTITION_FAT32:
431 case PARTITION_FAT32_XINT13:
432 *FsType = FS_FAT;
433 return TRUE;
434 case PARTITION_EXT2:
435 *FsType = FS_EXT2;
436 return TRUE;
437 case PARTITION_NTFS:
438 *FsType = FS_NTFS;
439 return TRUE;
440 default:
441 *FsType = 0;
442 return FALSE;
443 }
444
445 return FALSE;
446 }
447
448 BOOLEAN
449 i386DiskGetBootPath(char *BootPath, unsigned Size)
450 {
451 static char Path[] = "multi(0)disk(0)";
452 char Device[4];
453
454 _itoa(i386BootDrive, Device, 10);
455 if (Size <= sizeof(Path) + 6 + strlen(Device))
456 {
457 return FALSE;
458 }
459 strcpy(BootPath, Path);
460 strcat(BootPath, MachDiskBootingFromFloppy() ? "fdisk" : "cdrom");
461 strcat(strcat(strcat(BootPath, "("), Device), ")");
462
463 return TRUE;
464 }
465
466 BOOLEAN
467 i386DiskNormalizeSystemPath(char *SystemPath, unsigned Size)
468 {
469 CHAR BootPath[256];
470 ULONG PartitionNumber;
471 ULONG DriveNumber;
472 PARTITION_TABLE_ENTRY PartEntry;
473 char *p;
474
475 if (!DissectArcPath(SystemPath, BootPath, &DriveNumber, &PartitionNumber))
476 {
477 return FALSE;
478 }
479
480 if (0 != PartitionNumber)
481 {
482 return TRUE;
483 }
484
485 if (! DiskGetActivePartitionEntry(DriveNumber,
486 &PartEntry,
487 &PartitionNumber) ||
488 PartitionNumber < 1 || 9 < PartitionNumber)
489 {
490 return FALSE;
491 }
492
493 p = SystemPath;
494 while ('\0' != *p && 0 != _strnicmp(p, "partition(", 10)) {
495 p++;
496 }
497 p = strchr(p, ')');
498 if (NULL == p || '0' != *(p - 1)) {
499 return FALSE;
500 }
501 *(p - 1) = '0' + PartitionNumber;
502
503 return TRUE;
504 }
505
506 #endif /* defined __i386__ */
507
508 /* EOF */