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