xref: /reactos/boot/freeldr/freeldr/disk/partition.c (revision bee8e81d)
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
DiskReadBootRecord(IN UCHAR DriveNumber,IN ULONGLONG LogicalSectorNumber,OUT PMASTER_BOOT_RECORD BootRecord)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
DiskGetFirstPartitionEntry(IN PMASTER_BOOT_RECORD MasterBootRecord,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)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
DiskGetFirstExtendedPartitionEntry(IN PMASTER_BOOT_RECORD MasterBootRecord,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)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
DiskGetActivePartitionEntry(IN UCHAR DriveNumber,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry,OUT PULONG ActivePartition)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
DiskGetMbrPartitionEntry(IN UCHAR DriveNumber,IN ULONG PartitionNumber,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)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
DiskGetBrfrPartitionEntry(IN UCHAR DriveNumber,IN ULONG PartitionNumber,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)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         RtlZeroMemory(PartitionTableEntry, 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
DiskDetectPartitionType(IN UCHAR DriveNumber)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
DiskGetBootPartitionEntry(IN UCHAR DriveNumber,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry,OUT PULONG BootPartition)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
DiskGetPartitionEntry(IN UCHAR DriveNumber,IN ULONG PartitionNumber,OUT PPARTITION_TABLE_ENTRY PartitionTableEntry)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 #endif
442