[FREELDR] Fix 64 bit issues
[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 /*
21 * TODO: This is here where we should add support for GPT partitions
22 * as well as partitionless disks!
23 */
24
25 #ifndef _M_ARM
26 #include <freeldr.h>
27
28 #include <debug.h>
29
30 DBG_DEFAULT_CHANNEL(DISK);
31
32 /* This function serves to retrieve a partition entry for devices that handle partitions differently */
33 DISK_GET_PARTITION_ENTRY DiskGetPartitionEntry = DiskGetMbrPartitionEntry;
34
35 BOOLEAN DiskGetActivePartitionEntry(UCHAR DriveNumber,
36 PPARTITION_TABLE_ENTRY PartitionTableEntry,
37 ULONG *ActivePartition)
38 {
39 ULONG BootablePartitionCount = 0;
40 ULONG CurrentPartitionNumber;
41 ULONG Index;
42 MASTER_BOOT_RECORD MasterBootRecord;
43 PPARTITION_TABLE_ENTRY ThisPartitionTableEntry;
44
45 *ActivePartition = 0;
46
47 // Read master boot record
48 if (!DiskReadBootRecord(DriveNumber, 0, &MasterBootRecord))
49 {
50 return FALSE;
51 }
52
53 CurrentPartitionNumber = 0;
54 for (Index=0; Index<4; Index++)
55 {
56 ThisPartitionTableEntry = &MasterBootRecord.PartitionTable[Index];
57
58 if (ThisPartitionTableEntry->SystemIndicator != PARTITION_ENTRY_UNUSED &&
59 ThisPartitionTableEntry->SystemIndicator != PARTITION_EXTENDED &&
60 ThisPartitionTableEntry->SystemIndicator != PARTITION_XINT13_EXTENDED)
61 {
62 CurrentPartitionNumber++;
63
64 // Test if this is the bootable partition
65 if (ThisPartitionTableEntry->BootIndicator == 0x80)
66 {
67 BootablePartitionCount++;
68 *ActivePartition = CurrentPartitionNumber;
69
70 // Copy the partition table entry
71 RtlCopyMemory(PartitionTableEntry,
72 ThisPartitionTableEntry,
73 sizeof(PARTITION_TABLE_ENTRY));
74 }
75 }
76 }
77
78 // Make sure there was only one bootable partition
79 if (BootablePartitionCount == 0)
80 {
81 ERR("No bootable (active) partitions found.\n");
82 return FALSE;
83 }
84 else if (BootablePartitionCount != 1)
85 {
86 ERR("Too many bootable (active) partitions found.\n");
87 return FALSE;
88 }
89
90 return TRUE;
91 }
92
93 BOOLEAN DiskGetMbrPartitionEntry(UCHAR DriveNumber, ULONG PartitionNumber, PPARTITION_TABLE_ENTRY PartitionTableEntry)
94 {
95 MASTER_BOOT_RECORD MasterBootRecord;
96 PARTITION_TABLE_ENTRY ExtendedPartitionTableEntry;
97 ULONG ExtendedPartitionNumber;
98 ULONG ExtendedPartitionOffset;
99 ULONG Index;
100 ULONG CurrentPartitionNumber;
101 PPARTITION_TABLE_ENTRY ThisPartitionTableEntry;
102
103 // Read master boot record
104 if (!DiskReadBootRecord(DriveNumber, 0, &MasterBootRecord))
105 {
106 return FALSE;
107 }
108
109 CurrentPartitionNumber = 0;
110 for (Index=0; Index<4; Index++)
111 {
112 ThisPartitionTableEntry = &MasterBootRecord.PartitionTable[Index];
113
114 if (ThisPartitionTableEntry->SystemIndicator != PARTITION_ENTRY_UNUSED &&
115 ThisPartitionTableEntry->SystemIndicator != PARTITION_EXTENDED &&
116 ThisPartitionTableEntry->SystemIndicator != PARTITION_XINT13_EXTENDED)
117 {
118 CurrentPartitionNumber++;
119 }
120
121 if (PartitionNumber == CurrentPartitionNumber)
122 {
123 RtlCopyMemory(PartitionTableEntry, ThisPartitionTableEntry, sizeof(PARTITION_TABLE_ENTRY));
124 return TRUE;
125 }
126 }
127
128 // They want an extended partition entry so we will need
129 // to loop through all the extended partitions on the disk
130 // and return the one they want.
131
132 ExtendedPartitionNumber = PartitionNumber - CurrentPartitionNumber - 1;
133
134 // Set the initial relative starting sector to 0
135 // This is because extended partition starting
136 // sectors a numbered relative to their parent
137 ExtendedPartitionOffset = 0;
138
139 for (Index=0; Index<=ExtendedPartitionNumber; Index++)
140 {
141 // Get the extended partition table entry
142 if (!DiskGetFirstExtendedPartitionEntry(&MasterBootRecord, &ExtendedPartitionTableEntry))
143 {
144 return FALSE;
145 }
146
147 // Adjust the relative starting sector of the partition
148 ExtendedPartitionTableEntry.SectorCountBeforePartition += ExtendedPartitionOffset;
149 if (ExtendedPartitionOffset == 0)
150 {
151 // Set the start of the parrent extended partition
152 ExtendedPartitionOffset = ExtendedPartitionTableEntry.SectorCountBeforePartition;
153 }
154 // Read the partition boot record
155 if (!DiskReadBootRecord(DriveNumber, ExtendedPartitionTableEntry.SectorCountBeforePartition, &MasterBootRecord))
156 {
157 return FALSE;
158 }
159
160 // Get the first real partition table entry
161 if (!DiskGetFirstPartitionEntry(&MasterBootRecord, PartitionTableEntry))
162 {
163 return FALSE;
164 }
165
166 // Now correct the start sector of the partition
167 PartitionTableEntry->SectorCountBeforePartition += ExtendedPartitionTableEntry.SectorCountBeforePartition;
168 }
169
170 // When we get here we should have the correct entry
171 // already stored in PartitionTableEntry
172 // so just return TRUE
173 return TRUE;
174 }
175
176 BOOLEAN DiskGetFirstPartitionEntry(PMASTER_BOOT_RECORD MasterBootRecord, PPARTITION_TABLE_ENTRY PartitionTableEntry)
177 {
178 ULONG Index;
179
180 for (Index=0; Index<4; Index++)
181 {
182 // Check the system indicator
183 // If it's not an extended or unused partition
184 // then we're done
185 if ((MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_ENTRY_UNUSED) &&
186 (MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_EXTENDED) &&
187 (MasterBootRecord->PartitionTable[Index].SystemIndicator != PARTITION_XINT13_EXTENDED))
188 {
189 RtlCopyMemory(PartitionTableEntry, &MasterBootRecord->PartitionTable[Index], sizeof(PARTITION_TABLE_ENTRY));
190 return TRUE;
191 }
192 }
193
194 return FALSE;
195 }
196
197 BOOLEAN DiskGetFirstExtendedPartitionEntry(PMASTER_BOOT_RECORD MasterBootRecord, PPARTITION_TABLE_ENTRY PartitionTableEntry)
198 {
199 ULONG Index;
200
201 for (Index=0; Index<4; Index++)
202 {
203 // Check the system indicator
204 // If it an extended partition then we're done
205 if ((MasterBootRecord->PartitionTable[Index].SystemIndicator == PARTITION_EXTENDED) ||
206 (MasterBootRecord->PartitionTable[Index].SystemIndicator == PARTITION_XINT13_EXTENDED))
207 {
208 RtlCopyMemory(PartitionTableEntry, &MasterBootRecord->PartitionTable[Index], sizeof(PARTITION_TABLE_ENTRY));
209 return TRUE;
210 }
211 }
212
213 return FALSE;
214 }
215
216 BOOLEAN DiskReadBootRecord(UCHAR DriveNumber, ULONGLONG LogicalSectorNumber, PMASTER_BOOT_RECORD BootRecord)
217 {
218 ULONG Index;
219
220 // Read master boot record
221 if (!MachDiskReadLogicalSectors(DriveNumber, LogicalSectorNumber, 1, DiskReadBuffer))
222 {
223 return FALSE;
224 }
225 RtlCopyMemory(BootRecord, DiskReadBuffer, sizeof(MASTER_BOOT_RECORD));
226
227 TRACE("Dumping partition table for drive 0x%x:\n", DriveNumber);
228 TRACE("Boot record logical start sector = %d\n", LogicalSectorNumber);
229 TRACE("sizeof(MASTER_BOOT_RECORD) = 0x%x.\n", sizeof(MASTER_BOOT_RECORD));
230
231 for (Index=0; Index<4; Index++)
232 {
233 TRACE("-------------------------------------------\n");
234 TRACE("Partition %d\n", (Index + 1));
235 TRACE("BootIndicator: 0x%x\n", BootRecord->PartitionTable[Index].BootIndicator);
236 TRACE("StartHead: 0x%x\n", BootRecord->PartitionTable[Index].StartHead);
237 TRACE("StartSector (Plus 2 cylinder bits): 0x%x\n", BootRecord->PartitionTable[Index].StartSector);
238 TRACE("StartCylinder: 0x%x\n", BootRecord->PartitionTable[Index].StartCylinder);
239 TRACE("SystemIndicator: 0x%x\n", BootRecord->PartitionTable[Index].SystemIndicator);
240 TRACE("EndHead: 0x%x\n", BootRecord->PartitionTable[Index].EndHead);
241 TRACE("EndSector (Plus 2 cylinder bits): 0x%x\n", BootRecord->PartitionTable[Index].EndSector);
242 TRACE("EndCylinder: 0x%x\n", BootRecord->PartitionTable[Index].EndCylinder);
243 TRACE("SectorCountBeforePartition: 0x%x\n", BootRecord->PartitionTable[Index].SectorCountBeforePartition);
244 TRACE("PartitionSectorCount: 0x%x\n", BootRecord->PartitionTable[Index].PartitionSectorCount);
245 }
246
247 // Check the partition table magic value
248 if (BootRecord->MasterBootRecordMagic != 0xaa55)
249 {
250 return FALSE;
251 }
252
253 return TRUE;
254 }
255
256 #ifndef _M_AMD64
257 NTSTATUS
258 NTAPI
259 IopReadBootRecord(
260 IN PDEVICE_OBJECT DeviceObject,
261 IN ULONGLONG LogicalSectorNumber,
262 IN ULONG SectorSize,
263 OUT PMASTER_BOOT_RECORD BootRecord)
264 {
265 ULONG_PTR FileId = (ULONG_PTR)DeviceObject;
266 LARGE_INTEGER Position;
267 ULONG BytesRead;
268 ARC_STATUS Status;
269
270 Position.QuadPart = LogicalSectorNumber * SectorSize;
271 Status = ArcSeek(FileId, &Position, SeekAbsolute);
272 if (Status != ESUCCESS)
273 return STATUS_IO_DEVICE_ERROR;
274
275 Status = ArcRead(FileId, BootRecord, SectorSize, &BytesRead);
276 if (Status != ESUCCESS || BytesRead != SectorSize)
277 return STATUS_IO_DEVICE_ERROR;
278
279 return STATUS_SUCCESS;
280 }
281
282 BOOLEAN
283 NTAPI
284 IopCopyPartitionRecord(
285 IN BOOLEAN ReturnRecognizedPartitions,
286 IN ULONG SectorSize,
287 IN PPARTITION_TABLE_ENTRY PartitionTableEntry,
288 OUT PARTITION_INFORMATION *PartitionEntry)
289 {
290 BOOLEAN IsRecognized;
291
292 IsRecognized = TRUE; /* FIXME */
293 if (!IsRecognized && ReturnRecognizedPartitions)
294 return FALSE;
295
296 PartitionEntry->StartingOffset.QuadPart = (ULONGLONG)PartitionTableEntry->SectorCountBeforePartition * SectorSize;
297 PartitionEntry->PartitionLength.QuadPart = (ULONGLONG)PartitionTableEntry->PartitionSectorCount * SectorSize;
298 PartitionEntry->HiddenSectors = 0;
299 PartitionEntry->PartitionNumber = 0; /* Will be filled later */
300 PartitionEntry->PartitionType = PartitionTableEntry->SystemIndicator;
301 PartitionEntry->BootIndicator = (PartitionTableEntry->BootIndicator & 0x80) ? TRUE : FALSE;
302 PartitionEntry->RecognizedPartition = IsRecognized;
303 PartitionEntry->RewritePartition = FALSE;
304
305 return TRUE;
306 }
307
308 NTSTATUS
309 FASTCALL
310 IoReadPartitionTable(
311 IN PDEVICE_OBJECT DeviceObject,
312 IN ULONG SectorSize,
313 IN BOOLEAN ReturnRecognizedPartitions,
314 OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
315 {
316 PMASTER_BOOT_RECORD MasterBootRecord;
317 PDRIVE_LAYOUT_INFORMATION Partitions;
318 ULONG NbPartitions, i, Size;
319 NTSTATUS ret;
320
321 *PartitionBuffer = NULL;
322
323 if (SectorSize < sizeof(MASTER_BOOT_RECORD))
324 return STATUS_NOT_SUPPORTED;
325
326 MasterBootRecord = ExAllocatePool(NonPagedPool, SectorSize);
327 if (!MasterBootRecord)
328 return STATUS_NO_MEMORY;
329
330 /* Read disk MBR */
331 ret = IopReadBootRecord(DeviceObject, 0, SectorSize, MasterBootRecord);
332 if (!NT_SUCCESS(ret))
333 {
334 ExFreePool(MasterBootRecord);
335 return ret;
336 }
337
338 /* Check validity of boot record */
339 if (MasterBootRecord->MasterBootRecordMagic != 0xaa55)
340 {
341 ExFreePool(MasterBootRecord);
342 return STATUS_NOT_SUPPORTED;
343 }
344
345 /* Count number of partitions */
346 NbPartitions = 0;
347 for (i = 0; i < 4; i++)
348 {
349 NbPartitions++;
350
351 if (MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_EXTENDED ||
352 MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_XINT13_EXTENDED)
353 {
354 /* FIXME: unhandled case; count number of partitions */
355 UNIMPLEMENTED;
356 }
357 }
358
359 if (NbPartitions == 0)
360 {
361 ExFreePool(MasterBootRecord);
362 return STATUS_NOT_SUPPORTED;
363 }
364
365 /* Allocation space to store partitions */
366 Size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
367 NbPartitions * sizeof(PARTITION_INFORMATION);
368 Partitions = ExAllocatePool(NonPagedPool, Size);
369 if (!Partitions)
370 {
371 ExFreePool(MasterBootRecord);
372 return STATUS_NO_MEMORY;
373 }
374
375 /* Count number of partitions */
376 NbPartitions = 0;
377 for (i = 0; i < 4; i++)
378 {
379 if (IopCopyPartitionRecord(ReturnRecognizedPartitions,
380 SectorSize,
381 &MasterBootRecord->PartitionTable[i],
382 &Partitions->PartitionEntry[NbPartitions]))
383 {
384 Partitions->PartitionEntry[NbPartitions].PartitionNumber = NbPartitions + 1;
385 NbPartitions++;
386 }
387
388 if (MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_EXTENDED ||
389 MasterBootRecord->PartitionTable[i].SystemIndicator == PARTITION_XINT13_EXTENDED)
390 {
391 /* FIXME: unhandled case; copy partitions */
392 UNIMPLEMENTED;
393 }
394 }
395
396 Partitions->PartitionCount = NbPartitions;
397 Partitions->Signature = MasterBootRecord->Signature;
398 ExFreePool(MasterBootRecord);
399
400 *PartitionBuffer = Partitions;
401 return STATUS_SUCCESS;
402 }
403 #endif // _M_AMD64
404 #endif