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