[FREELDR] Other enhancements.
[reactos.git] / boot / freeldr / freeldr / disk / partition.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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef _M_ARM
21 #include <freeldr.h>
22
23 #include <debug.h>
24 DBG_DEFAULT_CHANNEL(DISK);
25
26 #define MaxDriveNumber 0xFF
27 static PARTITION_STYLE DiskPartitionType[MaxDriveNumber + 1];
28
29 /* BRFR signature at disk offset 0x600 */
30 #define XBOX_SIGNATURE_SECTOR 3
31 #define XBOX_SIGNATURE ('B' | ('R' << 8) | ('F' << 16) | ('R' << 24))
32
33 /* Default hardcoded partition number to boot from Xbox disk */
34 #define FATX_DATA_PARTITION 1
35
36 static struct
37 {
38 ULONG SectorCountBeforePartition;
39 ULONG PartitionSectorCount;
40 UCHAR SystemIndicator;
41 } XboxPartitions[] =
42 {
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: */
49 };
50
51 static BOOLEAN
52 DiskReadBootRecord(
53 IN UCHAR DriveNumber,
54 IN ULONGLONG LogicalSectorNumber,
55 OUT PMASTER_BOOT_RECORD BootRecord)
56 {
57 ULONG Index;
58
59 /* Read master boot record */
60 if (!MachDiskReadLogicalSectors(DriveNumber, LogicalSectorNumber, 1, DiskReadBuffer))
61 {
62 return FALSE;
63 }
64 RtlCopyMemory(BootRecord, DiskReadBuffer, sizeof(MASTER_BOOT_RECORD));
65
66 TRACE("Dumping partition table for drive 0x%x:\n", DriveNumber);
67 TRACE("Boot record logical start sector = %d\n", LogicalSectorNumber);
68 TRACE("sizeof(MASTER_BOOT_RECORD) = 0x%x.\n", sizeof(MASTER_BOOT_RECORD));
69
70 for (Index = 0; Index < 4; Index++)
71 {
72 TRACE("-------------------------------------------\n");
73 TRACE("Partition %d\n", (Index + 1));
74 TRACE("BootIndicator: 0x%x\n", BootRecord->PartitionTable[Index].BootIndicator);
75 TRACE("StartHead: 0x%x\n", BootRecord->PartitionTable[Index].StartHead);
76 TRACE("StartSector (Plus 2 cylinder bits): 0x%x\n", BootRecord->PartitionTable[Index].StartSector);
77 TRACE("StartCylinder: 0x%x\n", BootRecord->PartitionTable[Index].StartCylinder);
78 TRACE("SystemIndicator: 0x%x\n", BootRecord->PartitionTable[Index].SystemIndicator);
79 TRACE("EndHead: 0x%x\n", BootRecord->PartitionTable[Index].EndHead);
80 TRACE("EndSector (Plus 2 cylinder bits): 0x%x\n", BootRecord->PartitionTable[Index].EndSector);
81 TRACE("EndCylinder: 0x%x\n", BootRecord->PartitionTable[Index].EndCylinder);
82 TRACE("SectorCountBeforePartition: 0x%x\n", BootRecord->PartitionTable[Index].SectorCountBeforePartition);
83 TRACE("PartitionSectorCount: 0x%x\n", BootRecord->PartitionTable[Index].PartitionSectorCount);
84 }
85
86 /* Check the partition table magic value */
87 return (BootRecord->MasterBootRecordMagic == 0xaa55);
88 }
89
90 static BOOLEAN
91 DiskGetFirstPartitionEntry(
92 IN PMASTER_BOOT_RECORD MasterBootRecord,
93 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)
94 {
95 ULONG Index;
96
97 for (Index = 0; Index < 4; Index++)
98 {
99 /* Check the system indicator. If it's not an extended or unused partition then we're done. */
100 if ((MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_ENTRY_UNUSED) &&
101 (MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_EXTENDED) &&
102 (MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_XINT13_EXTENDED))
103 {
104 RtlCopyMemory(PartitionTableEntry, &MasterBootRecord->PartitionTable[Index], sizeof(PARTITION_TABLE_ENTRY));
105 return TRUE;
106 }
107 }
108
109 return FALSE;
110 }
111
112 static BOOLEAN
113 DiskGetFirstExtendedPartitionEntry(
114 IN PMASTER_BOOT_RECORD MasterBootRecord,
115 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)
116 {
117 ULONG Index;
118
119 for (Index = 0; Index < 4; Index++)
120 {
121 /* Check the system indicator. If it an extended partition then we're done. */
122 if ((MasterBootRecord->PartitionTable[Index].SystemIndicator == PARTITION_EXTENDED) ||
123 (MasterBootRecord->PartitionTable[Index].SystemIndicator == PARTITION_XINT13_EXTENDED))
124 {
125 RtlCopyMemory(PartitionTableEntry, &MasterBootRecord->PartitionTable[Index], sizeof(PARTITION_TABLE_ENTRY));
126 return TRUE;
127 }
128 }
129
130 return FALSE;
131 }
132
133 static BOOLEAN
134 DiskGetActivePartitionEntry(
135 IN UCHAR DriveNumber,
136 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry,
137 OUT PULONG ActivePartition)
138 {
139 ULONG BootablePartitionCount = 0;
140 ULONG CurrentPartitionNumber;
141 ULONG Index;
142 MASTER_BOOT_RECORD MasterBootRecord;
143 PPARTITION_TABLE_ENTRY ThisPartitionTableEntry;
144
145 *ActivePartition = 0;
146
147 /* Read master boot record */
148 if (!DiskReadBootRecord(DriveNumber, 0, &MasterBootRecord))
149 {
150 return FALSE;
151 }
152
153 CurrentPartitionNumber = 0;
154 for (Index = 0; Index < 4; Index++)
155 {
156 ThisPartitionTableEntry = &MasterBootRecord.PartitionTable[Index];
157
158 if (ThisPartitionTableEntry->SystemIndicator != PARTITION_ENTRY_UNUSED &&
159 ThisPartitionTableEntry->SystemIndicator != PARTITION_EXTENDED &&
160 ThisPartitionTableEntry->SystemIndicator != PARTITION_XINT13_EXTENDED)
161 {
162 CurrentPartitionNumber++;
163
164 /* Test if this is the bootable partition */
165 if (ThisPartitionTableEntry->BootIndicator == 0x80)
166 {
167 BootablePartitionCount++;
168 *ActivePartition = CurrentPartitionNumber;
169
170 /* Copy the partition table entry */
171 RtlCopyMemory(PartitionTableEntry,
172 ThisPartitionTableEntry,
173 sizeof(PARTITION_TABLE_ENTRY));
174 }
175 }
176 }
177
178 /* Make sure there was only one bootable partition */
179 if (BootablePartitionCount == 0)
180 {
181 ERR("No bootable (active) partitions found.\n");
182 return FALSE;
183 }
184 else if (BootablePartitionCount != 1)
185 {
186 ERR("Too many bootable (active) partitions found.\n");
187 return FALSE;
188 }
189
190 return TRUE;
191 }
192
193 static BOOLEAN
194 DiskGetMbrPartitionEntry(
195 IN UCHAR DriveNumber,
196 IN ULONG PartitionNumber,
197 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)
198 {
199 MASTER_BOOT_RECORD MasterBootRecord;
200 PARTITION_TABLE_ENTRY ExtendedPartitionTableEntry;
201 ULONG ExtendedPartitionNumber;
202 ULONG ExtendedPartitionOffset;
203 ULONG Index;
204 ULONG CurrentPartitionNumber;
205 PPARTITION_TABLE_ENTRY ThisPartitionTableEntry;
206
207 /* Read master boot record */
208 if (!DiskReadBootRecord(DriveNumber, 0, &MasterBootRecord))
209 {
210 return FALSE;
211 }
212
213 CurrentPartitionNumber = 0;
214 for (Index = 0; Index < 4; Index++)
215 {
216 ThisPartitionTableEntry = &MasterBootRecord.PartitionTable[Index];
217
218 if (ThisPartitionTableEntry->SystemIndicator != PARTITION_ENTRY_UNUSED &&
219 ThisPartitionTableEntry->SystemIndicator != PARTITION_EXTENDED &&
220 ThisPartitionTableEntry->SystemIndicator != PARTITION_XINT13_EXTENDED)
221 {
222 CurrentPartitionNumber++;
223 }
224
225 if (PartitionNumber == CurrentPartitionNumber)
226 {
227 RtlCopyMemory(PartitionTableEntry, ThisPartitionTableEntry, sizeof(PARTITION_TABLE_ENTRY));
228 return TRUE;
229 }
230 }
231
232 /*
233 * They want an extended partition entry so we will need
234 * to loop through all the extended partitions on the disk
235 * and return the one they want.
236 */
237 ExtendedPartitionNumber = PartitionNumber - CurrentPartitionNumber - 1;
238
239 /*
240 * Set the initial relative starting sector to 0.
241 * This is because extended partition starting
242 * sectors a numbered relative to their parent.
243 */
244 ExtendedPartitionOffset = 0;
245
246 for (Index = 0; Index <= ExtendedPartitionNumber; Index++)
247 {
248 /* Get the extended partition table entry */
249 if (!DiskGetFirstExtendedPartitionEntry(&MasterBootRecord, &ExtendedPartitionTableEntry))
250 {
251 return FALSE;
252 }
253
254 /* Adjust the relative starting sector of the partition */
255 ExtendedPartitionTableEntry.SectorCountBeforePartition += ExtendedPartitionOffset;
256 if (ExtendedPartitionOffset == 0)
257 {
258 /* Set the start of the parrent extended partition */
259 ExtendedPartitionOffset = ExtendedPartitionTableEntry.SectorCountBeforePartition;
260 }
261 /* Read the partition boot record */
262 if (!DiskReadBootRecord(DriveNumber, ExtendedPartitionTableEntry.SectorCountBeforePartition, &MasterBootRecord))
263 {
264 return FALSE;
265 }
266
267 /* Get the first real partition table entry */
268 if (!DiskGetFirstPartitionEntry(&MasterBootRecord, PartitionTableEntry))
269 {
270 return FALSE;
271 }
272
273 /* Now correct the start sector of the partition */
274 PartitionTableEntry->SectorCountBeforePartition += ExtendedPartitionTableEntry.SectorCountBeforePartition;
275 }
276
277 /*
278 * When we get here we should have the correct entry already
279 * stored in PartitionTableEntry, so just return TRUE.
280 */
281 return TRUE;
282 }
283
284 static BOOLEAN
285 DiskGetBrfrPartitionEntry(
286 IN UCHAR DriveNumber,
287 IN ULONG PartitionNumber,
288 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)
289 {
290 /*
291 * Get partition entry of an Xbox-standard BRFR partitioned disk.
292 */
293 if (PartitionNumber >= 1 && PartitionNumber <= sizeof(XboxPartitions) / sizeof(XboxPartitions[0]) &&
294 MachDiskReadLogicalSectors(DriveNumber, XBOX_SIGNATURE_SECTOR, 1, DiskReadBuffer))
295 {
296 if (*((PULONG)DiskReadBuffer) != XBOX_SIGNATURE)
297 {
298 /* No magic Xbox partitions */
299 return FALSE;
300 }
301
302 memset(PartitionTableEntry, 0, sizeof(PARTITION_TABLE_ENTRY));
303 PartitionTableEntry->SystemIndicator = XboxPartitions[PartitionNumber - 1].SystemIndicator;
304 PartitionTableEntry->SectorCountBeforePartition = XboxPartitions[PartitionNumber - 1].SectorCountBeforePartition;
305 PartitionTableEntry->PartitionSectorCount = XboxPartitions[PartitionNumber - 1].PartitionSectorCount;
306 return TRUE;
307 }
308
309 /* Partition does not exist */
310 return FALSE;
311 }
312
313 VOID
314 DiskDetectPartitionType(
315 IN UCHAR DriveNumber)
316 {
317 MASTER_BOOT_RECORD MasterBootRecord;
318 ULONG Index;
319 ULONG PartitionCount = 0;
320 PPARTITION_TABLE_ENTRY ThisPartitionTableEntry;
321 BOOLEAN GPTProtect = FALSE;
322 PARTITION_TABLE_ENTRY PartitionTableEntry;
323
324 /* Probe for Master Boot Record */
325 if (DiskReadBootRecord(DriveNumber, 0, &MasterBootRecord))
326 {
327 DiskPartitionType[DriveNumber] = PARTITION_STYLE_MBR;
328
329 /* Check for GUID Partition Table */
330 for (Index = 0; Index < 4; Index++)
331 {
332 ThisPartitionTableEntry = &MasterBootRecord.PartitionTable[Index];
333
334 if (ThisPartitionTableEntry->SystemIndicator != PARTITION_ENTRY_UNUSED)
335 {
336 PartitionCount++;
337
338 if (Index == 0 && ThisPartitionTableEntry->SystemIndicator == PARTITION_GPT)
339 {
340 GPTProtect = TRUE;
341 }
342 }
343 }
344
345 if (PartitionCount == 1 && GPTProtect)
346 {
347 DiskPartitionType[DriveNumber] = PARTITION_STYLE_GPT;
348 }
349 TRACE("Drive 0x%X partition type %s\n", DriveNumber, DiskPartitionType[DriveNumber] == PARTITION_STYLE_MBR ? "MBR" : "GPT");
350 return;
351 }
352
353 /* Probe for Xbox-BRFR partitioning */
354 if (DiskGetBrfrPartitionEntry(DriveNumber, FATX_DATA_PARTITION, &PartitionTableEntry))
355 {
356 DiskPartitionType[DriveNumber] = PARTITION_STYLE_BRFR;
357 TRACE("Drive 0x%X partition type Xbox-BRFR\n", DriveNumber);
358 return;
359 }
360
361 /* Failed to detect partitions, assume partitionless disk */
362 DiskPartitionType[DriveNumber] = PARTITION_STYLE_RAW;
363 TRACE("Drive 0x%X partition type unknown\n", DriveNumber);
364 }
365
366 BOOLEAN
367 DiskGetBootPartitionEntry(
368 IN UCHAR DriveNumber,
369 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry,
370 OUT PULONG BootPartition)
371 {
372 switch (DiskPartitionType[DriveNumber])
373 {
374 case PARTITION_STYLE_MBR:
375 {
376 return DiskGetActivePartitionEntry(DriveNumber, PartitionTableEntry, BootPartition);
377 }
378 case PARTITION_STYLE_GPT:
379 {
380 FIXME("DiskGetBootPartitionEntry() unimplemented for GPT\n");
381 return FALSE;
382 }
383 case PARTITION_STYLE_RAW:
384 {
385 FIXME("DiskGetBootPartitionEntry() unimplemented for RAW\n");
386 return FALSE;
387 }
388 case PARTITION_STYLE_BRFR:
389 {
390 if (DiskGetBrfrPartitionEntry(DriveNumber, FATX_DATA_PARTITION, PartitionTableEntry))
391 {
392 *BootPartition = FATX_DATA_PARTITION;
393 return TRUE;
394 }
395 return FALSE;
396 }
397 default:
398 {
399 ERR("Drive 0x%X partition type = %d, should not happen!\n", DriveNumber, DiskPartitionType[DriveNumber]);
400 ASSERT(FALSE);
401 }
402 }
403 return FALSE;
404 }
405
406 BOOLEAN
407 DiskGetPartitionEntry(
408 IN UCHAR DriveNumber,
409 IN ULONG PartitionNumber,
410 OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)
411 {
412 switch (DiskPartitionType[DriveNumber])
413 {
414 case PARTITION_STYLE_MBR:
415 {
416 return DiskGetMbrPartitionEntry(DriveNumber, PartitionNumber, PartitionTableEntry);
417 }
418 case PARTITION_STYLE_GPT:
419 {
420 FIXME("DiskGetPartitionEntry() unimplemented for GPT\n");
421 return FALSE;
422 }
423 case PARTITION_STYLE_RAW:
424 {
425 FIXME("DiskGetPartitionEntry() unimplemented for RAW\n");
426 return FALSE;
427 }
428 case PARTITION_STYLE_BRFR:
429 {
430 return DiskGetBrfrPartitionEntry(DriveNumber, PartitionNumber, PartitionTableEntry);
431 }
432 default:
433 {
434 ERR("Drive 0x%X partition type = %d, should not happen!\n", DriveNumber, DiskPartitionType[DriveNumber]);
435 ASSERT(FALSE);
436 }
437 }
438 return FALSE;
439 }
440
441 #ifndef _M_AMD64
442 NTSTATUS
443 NTAPI
444 IopReadBootRecord(
445 IN PDEVICE_OBJECT DeviceObject,
446 IN ULONGLONG LogicalSectorNumber,
447 IN ULONG SectorSize,
448 OUT PMASTER_BOOT_RECORD BootRecord)
449 {
450 ULONG_PTR FileId = (ULONG_PTR)DeviceObject;
451 LARGE_INTEGER Position;
452 ULONG BytesRead;
453 ARC_STATUS Status;
454
455 Position.QuadPart = LogicalSectorNumber * SectorSize;
456 Status = ArcSeek(FileId, &Position, SeekAbsolute);
457 if (Status != ESUCCESS)
458 return STATUS_IO_DEVICE_ERROR;
459
460 Status = ArcRead(FileId, BootRecord, SectorSize, &BytesRead);
461 if (Status != ESUCCESS || BytesRead != SectorSize)
462 return STATUS_IO_DEVICE_ERROR;
463
464 return STATUS_SUCCESS;
465 }
466
467 BOOLEAN
468 NTAPI
469 IopCopyPartitionRecord(
470 IN BOOLEAN ReturnRecognizedPartitions,
471 IN ULONG SectorSize,
472 IN PPARTITION_TABLE_ENTRY PartitionTableEntry,
473 OUT PARTITION_INFORMATION *PartitionEntry)
474 {
475 BOOLEAN IsRecognized;
476
477 IsRecognized = TRUE; /* FIXME */
478 if (!IsRecognized && ReturnRecognizedPartitions)
479 return FALSE;
480
481 PartitionEntry->StartingOffset.QuadPart = (ULONGLONG)PartitionTableEntry->SectorCountBeforePartition * SectorSize;
482 PartitionEntry->PartitionLength.QuadPart = (ULONGLONG)PartitionTableEntry->PartitionSectorCount * SectorSize;
483 PartitionEntry->HiddenSectors = 0;
484 PartitionEntry->PartitionNumber = 0; /* Will be filled later */
485 PartitionEntry->PartitionType = PartitionTableEntry->SystemIndicator;
486 PartitionEntry->BootIndicator = (PartitionTableEntry->BootIndicator & 0x80) ? TRUE : FALSE;
487 PartitionEntry->RecognizedPartition = IsRecognized;
488 PartitionEntry->RewritePartition = FALSE;
489
490 return TRUE;
491 }
492
493 NTSTATUS
494 FASTCALL
495 IoReadPartitionTable(
496 IN PDEVICE_OBJECT DeviceObject,
497 IN ULONG SectorSize,
498 IN BOOLEAN ReturnRecognizedPartitions,
499 OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
500 {
501 NTSTATUS Status;
502 PMASTER_BOOT_RECORD MasterBootRecord;
503 PDRIVE_LAYOUT_INFORMATION Partitions;
504 ULONG NbPartitions, i, Size;
505
506 *PartitionBuffer = NULL;
507
508 if (SectorSize < sizeof(MASTER_BOOT_RECORD))
509 return STATUS_NOT_SUPPORTED;
510
511 MasterBootRecord = ExAllocatePool(NonPagedPool, SectorSize);
512 if (!MasterBootRecord)
513 return STATUS_NO_MEMORY;
514
515 /* Read disk MBR */
516 Status = IopReadBootRecord(DeviceObject, 0, SectorSize, MasterBootRecord);
517 if (!NT_SUCCESS(Status))
518 {
519 ExFreePool(MasterBootRecord);
520 return Status;
521 }
522
523 /* Check validity of boot record */
524 if (MasterBootRecord->MasterBootRecordMagic != 0xaa55)
525 {
526 ExFreePool(MasterBootRecord);
527 return STATUS_NOT_SUPPORTED;
528 }
529
530 /* Count number of partitions */
531 NbPartitions = 0;
532 for (i = 0; i < 4; i++)
533 {
534 NbPartitions++;
535
536 if (MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_EXTENDED ||
537 MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_XINT13_EXTENDED)
538 {
539 /* FIXME: unhandled case; count number of partitions */
540 UNIMPLEMENTED;
541 }
542 }
543
544 if (NbPartitions == 0)
545 {
546 ExFreePool(MasterBootRecord);
547 return STATUS_NOT_SUPPORTED;
548 }
549
550 /* Allocation space to store partitions */
551 Size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
552 NbPartitions * sizeof(PARTITION_INFORMATION);
553 Partitions = ExAllocatePool(NonPagedPool, Size);
554 if (!Partitions)
555 {
556 ExFreePool(MasterBootRecord);
557 return STATUS_NO_MEMORY;
558 }
559
560 /* Count number of partitions */
561 NbPartitions = 0;
562 for (i = 0; i < 4; i++)
563 {
564 if (IopCopyPartitionRecord(ReturnRecognizedPartitions,
565 SectorSize,
566 &MasterBootRecord->PartitionTable[i],
567 &Partitions->PartitionEntry[NbPartitions]))
568 {
569 Partitions->PartitionEntry[NbPartitions].PartitionNumber = NbPartitions + 1;
570 NbPartitions++;
571 }
572
573 if (MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_EXTENDED ||
574 MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_XINT13_EXTENDED)
575 {
576 /* FIXME: unhandled case; copy partitions */
577 UNIMPLEMENTED;
578 }
579 }
580
581 Partitions->PartitionCount = NbPartitions;
582 Partitions->Signature = MasterBootRecord->Signature;
583 ExFreePool(MasterBootRecord);
584
585 *PartitionBuffer = Partitions;
586 return STATUS_SUCCESS;
587 }
588 #endif // _M_AMD64
589 #endif