2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fstub/fstubex.c
5 * PURPOSE: Extended FSTUB Routines (not linked to HAL)
6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
17 typedef struct _DISK_INFORMATION
19 PDEVICE_OBJECT DeviceObject
;
21 DISK_GEOMETRY_EX DiskGeometry
;
23 ULONGLONG SectorCount
;
24 } DISK_INFORMATION
, *PDISK_INFORMATION
;
27 typedef struct _EFI_PARTITION_HEADER
29 ULONGLONG Signature
; // 0
31 ULONG HeaderSize
; // 12
32 ULONG HeaderCRC32
; // 16
34 ULONGLONG MyLBA
; // 24
35 ULONGLONG AlternateLBA
; // 32
36 ULONGLONG FirstUsableLBA
; // 40
37 ULONGLONG LastUsableLBA
; // 48
39 ULONGLONG PartitionEntryLBA
; // 72
40 ULONG NumberOfEntries
; // 80
41 ULONG SizeOfPartitionEntry
; // 84
42 ULONG PartitionEntryCRC32
; // 88
43 } EFI_PARTITION_HEADER
, *PEFI_PARTITION_HEADER
;
46 typedef struct _EFI_PARTITION_ENTRY
48 GUID PartitionType
; // 0
49 GUID UniquePartition
; // 16
50 ULONGLONG StartingLBA
; // 32
51 ULONGLONG EndingLBA
; // 40
52 ULONGLONG Attributes
; // 48
53 WCHAR Name
[0x24]; // 56
54 } EFI_PARTITION_ENTRY
, *PEFI_PARTITION_ENTRY
;
56 typedef struct _PARTITION_TABLE_ENTRY
62 UCHAR SystemIndicator
;
66 ULONG SectorCountBeforePartition
;
67 ULONG PartitionSectorCount
;
68 } PARTITION_TABLE_ENTRY
, *PPARTITION_TABLE_ENTRY
;
70 typedef struct _MASTER_BOOT_RECORD
72 UCHAR MasterBootRecordCodeAndData
[0x1B8]; // 0
73 ULONG Signature
; // 440
74 USHORT Reserved
; // 444
75 PARTITION_TABLE_ENTRY PartitionTable
[4]; // 446
76 USHORT MasterBootRecordMagic
; // 510
77 } MASTER_BOOT_RECORD
, *PMASTER_BOOT_RECORD
;
79 /* Tag for Fstub allocations */
80 #define TAG_FSTUB 'BtsF'
81 /* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */
82 #define PARTITION_ENTRY_SIZE 128
83 /* Defines "EFI PART" */
84 #define EFI_HEADER_SIGNATURE 0x5452415020494645ULL
85 /* Defines version 1.0 */
86 #define EFI_HEADER_REVISION_1 0x00010000
87 /* Defines system type for MBR showing that a GPT is following */
88 #define EFI_PMBR_OSTYPE_EFI 0xEE
90 #define IS_VALID_DISK_INFO(Disk) \
92 (Disk->DeviceObject) && \
93 (Disk->SectorSize) && \
99 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
100 IN ULONG PartitionNumber
105 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
106 IN PARTITION_STYLE
* PartitionStyle
111 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
116 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
117 OUT PDISK_GEOMETRY_EX Geometry
122 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
124 IN ULONGLONG StartingSector OPTIONAL
,
130 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
135 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
137 IN ULONG MaxPartitionCount
,
138 IN ULONGLONG FirstUsableLBA
,
139 IN ULONGLONG LastUsableLBA
,
140 IN BOOLEAN WriteBackupTable
,
141 IN ULONG PartitionCount
,
142 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
147 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
149 IN ULONGLONG StartingSector OPTIONAL
,
155 FstubAdjustPartitionCount(IN ULONG SectorSize
,
156 IN OUT PULONG PartitionCount
)
162 ASSERT(PartitionCount
);
164 /* Get partition count */
165 Count
= *PartitionCount
;
166 /* We need at least 128 entries */
172 /* Then, ensure that we will have a round value,
173 * ie, all sectors will be full of entries
174 * There won't be lonely entries
176 Count
= (Count
* PARTITION_ENTRY_SIZE
) / SectorSize
;
177 Count
= (Count
* SectorSize
) / PARTITION_ENTRY_SIZE
;
178 ASSERT(*PartitionCount
<= Count
);
180 *PartitionCount
= Count
;
182 /* One more sanity check */
183 if (SectorSize
== 512)
185 ASSERT(Count
% 4 == 0);
191 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject
,
192 OUT PDISK_INFORMATION
* DiskBuffer
,
193 PDISK_GEOMETRY_EX DiskGeometry OPTIONAL
)
196 PDISK_INFORMATION DiskInformation
;
199 ASSERT(DeviceObject
);
202 /* Allocate internal structure */
203 DiskInformation
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_INFORMATION
), TAG_FSTUB
);
204 if (!DiskInformation
)
206 return STATUS_INSUFFICIENT_RESOURCES
;
209 /* If caller don't pass needed information, let's get them */
212 Status
= FstubGetDiskGeometry(DeviceObject
, &(DiskInformation
->DiskGeometry
));
213 if (!NT_SUCCESS(Status
))
215 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
221 DiskInformation
->DiskGeometry
= *DiskGeometry
;
224 /* Ensure read/received information are correct */
225 if (DiskInformation
->DiskGeometry
.Geometry
.BytesPerSector
== 0 ||
226 DiskInformation
->DiskGeometry
.DiskSize
.QuadPart
== 0)
228 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
229 return STATUS_DEVICE_NOT_READY
;
232 /* Store vital information as well */
233 DiskInformation
->DeviceObject
= DeviceObject
;
234 DiskInformation
->SectorSize
= DiskInformation
->DiskGeometry
.Geometry
.BytesPerSector
;
235 DiskInformation
->SectorCount
= DiskInformation
->DiskGeometry
.DiskSize
.QuadPart
/ DiskInformation
->SectorSize
;
237 /* Finally, allocate the buffer that will be used for different read */
238 DiskInformation
->Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
, DiskInformation
->SectorSize
, TAG_FSTUB
);
239 if (!DiskInformation
->Buffer
)
241 ExFreePoolWithTag(DiskInformation
, TAG_FSTUB
);
242 return STATUS_INSUFFICIENT_RESOURCES
;
245 /* Return allocated internal structure */
246 *DiskBuffer
= DiskInformation
;
248 return STATUS_SUCCESS
;
251 PDRIVE_LAYOUT_INFORMATION
253 FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
256 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
261 /* Check whether we're dealing with MBR partition table */
262 if (LayoutEx
->PartitionStyle
!= PARTITION_STYLE_MBR
)
268 /* Allocate needed buffer */
269 DriveLayout
= ExAllocatePoolWithTag(NonPagedPool
,
270 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION
, PartitionEntry
) +
271 LayoutEx
->PartitionCount
* sizeof(PARTITION_INFORMATION
),
278 /* Convert information about partition table */
279 DriveLayout
->PartitionCount
= LayoutEx
->PartitionCount
;
280 DriveLayout
->Signature
= LayoutEx
->Mbr
.Signature
;
282 /* Convert each partition */
283 for (i
= 0; i
< LayoutEx
->PartitionCount
; i
++)
285 DriveLayout
->PartitionEntry
[i
].StartingOffset
= LayoutEx
->PartitionEntry
[i
].StartingOffset
;
286 DriveLayout
->PartitionEntry
[i
].PartitionLength
= LayoutEx
->PartitionEntry
[i
].PartitionLength
;
287 DriveLayout
->PartitionEntry
[i
].HiddenSectors
= LayoutEx
->PartitionEntry
[i
].Mbr
.HiddenSectors
;
288 DriveLayout
->PartitionEntry
[i
].PartitionNumber
= LayoutEx
->PartitionEntry
[i
].PartitionNumber
;
289 DriveLayout
->PartitionEntry
[i
].PartitionType
= LayoutEx
->PartitionEntry
[i
].Mbr
.PartitionType
;
290 DriveLayout
->PartitionEntry
[i
].BootIndicator
= LayoutEx
->PartitionEntry
[i
].Mbr
.BootIndicator
;
291 DriveLayout
->PartitionEntry
[i
].RecognizedPartition
= LayoutEx
->PartitionEntry
[i
].Mbr
.RecognizedPartition
;
292 DriveLayout
->PartitionEntry
[i
].RewritePartition
= LayoutEx
->PartitionEntry
[i
].RewritePartition
;
300 FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry
,
301 IN PPARTITION_INFORMATION_EX Partition
,
310 /* Just convert data to EFI partition entry type */
311 Entry
->PartitionType
= Partition
->Gpt
.PartitionType
;
312 Entry
->UniquePartition
= Partition
->Gpt
.PartitionId
;
313 Entry
->StartingLBA
= Partition
->StartingOffset
.QuadPart
/ SectorSize
;
314 Entry
->EndingLBA
= (Partition
->StartingOffset
.QuadPart
+ Partition
->PartitionLength
.QuadPart
- 1) / SectorSize
;
315 Entry
->Attributes
= Partition
->Gpt
.Attributes
;
316 RtlCopyMemory(Entry
->Name
, Partition
->Gpt
.Name
, sizeof(Entry
->Name
));
321 FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject
,
322 IN PCREATE_DISK_MBR DiskInfo
)
325 PDISK_INFORMATION Disk
= NULL
;
326 PMASTER_BOOT_RECORD MasterBootRecord
;
329 ASSERT(DeviceObject
);
331 /* Allocate internal structure */
332 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
333 if (!NT_SUCCESS(Status
))
338 /* Read previous MBR, if any */
339 Status
= FstubReadSector(Disk
->DeviceObject
,
343 if (!NT_SUCCESS(Status
))
345 FstubFreeDiskInformation(Disk
);
348 /* Fill the buffer with needed information, we won't overwrite boot code */
349 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
350 MasterBootRecord
->Signature
= DiskInfo
->Signature
;
351 RtlZeroMemory(MasterBootRecord
->PartitionTable
, sizeof(PARTITION_TABLE_ENTRY
) * 4);
352 MasterBootRecord
->MasterBootRecordMagic
= BOOT_RECORD_SIGNATURE
;
354 /* Finally, write MBR */
355 Status
= FstubWriteSector(Disk
->DeviceObject
,
360 /* Release internal structure and return */
361 FstubFreeDiskInformation(Disk
);
367 FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject
,
368 IN PCREATE_DISK_GPT DiskInfo
)
371 PDISK_INFORMATION Disk
= NULL
;
372 ULONGLONG FirstUsableLBA
, LastUsableLBA
;
373 ULONG MaxPartitionCount
, SectorsForPartitions
;
376 ASSERT(DeviceObject
);
379 /* Allocate internal structure */
380 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
381 if (!NT_SUCCESS(Status
))
387 /* Write legacy MBR */
388 Status
= FstubWriteBootSectorEFI(Disk
);
389 if (!NT_SUCCESS(Status
))
391 FstubFreeDiskInformation(Disk
);
395 /* Get max entries and adjust its number */
396 MaxPartitionCount
= DiskInfo
->MaxPartitionCount
;
397 FstubAdjustPartitionCount(Disk
->SectorSize
, &MaxPartitionCount
);
399 /* Count number of sectors needed to store partitions */
400 SectorsForPartitions
= (MaxPartitionCount
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
401 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
402 FirstUsableLBA
= SectorsForPartitions
+ 2;
403 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
404 LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
406 /* First, write primary table */
407 Status
= FstubWritePartitionTableEFI(Disk
,
415 /* Then, write backup table */
416 if (NT_SUCCESS(Status
))
418 Status
= FstubWritePartitionTableEFI(Disk
,
428 /* Release internal structure and return */
429 FstubFreeDiskInformation(Disk
);
435 FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject
)
438 PDISK_INFORMATION Disk
= NULL
;
439 PARTITION_STYLE PartitionStyle
;
440 PMASTER_BOOT_RECORD MasterBootRecord
;
443 ASSERT(DeviceObject
);
445 /* Allocate internal structure */
446 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
447 if (!NT_SUCCESS(Status
))
452 /* Detect current disk partition style */
453 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
454 if (!NT_SUCCESS(Status
))
456 FstubFreeDiskInformation(Disk
);
460 /* Read MBR, if any */
461 Status
= FstubReadSector(Disk
->DeviceObject
,
465 if (!NT_SUCCESS(Status
))
467 FstubFreeDiskInformation(Disk
);
471 /* Only zero useful stuff */
472 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
473 MasterBootRecord
->Signature
= 0;
474 RtlZeroMemory(MasterBootRecord
->PartitionTable
, sizeof(PARTITION_TABLE_ENTRY
));
475 MasterBootRecord
->MasterBootRecordMagic
= 0;
477 /* Write back that destroyed MBR */
478 Status
= FstubWriteSector(Disk
->DeviceObject
,
482 /* If previous style wasn't GPT, we're done here */
483 if (PartitionStyle
!= PARTITION_STYLE_GPT
)
485 FstubFreeDiskInformation(Disk
);
489 /* Otherwise, we've to zero the two GPT headers */
490 RtlZeroMemory(Disk
->Buffer
, Disk
->SectorSize
);
491 /* Erase primary header */
492 Status
= FstubWriteSector(Disk
->DeviceObject
,
496 /* In case of success, erase backup header */
497 if (NT_SUCCESS(Status
))
499 Status
= FstubWriteSector(Disk
->DeviceObject
,
501 Disk
->SectorCount
- 1ULL,
505 /* Release internal structure and return */
506 FstubFreeDiskInformation(Disk
);
512 FstubDbgGuidToString(IN PGUID Guid
,
516 "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
534 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
)
540 DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout
);
541 switch (DriveLayout
->PartitionStyle
)
543 case PARTITION_STYLE_MBR
:
544 if (DriveLayout
->PartitionCount
% 4 != 0)
546 DPRINT("Warning: Partition count isn't a 4-factor: %ld!\n", DriveLayout
->PartitionCount
);
549 DPRINT("Signature: %8.8x\n", DriveLayout
->Mbr
.Signature
);
550 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
552 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
556 case PARTITION_STYLE_GPT
:
557 FstubDbgGuidToString(&(DriveLayout
->Gpt
.DiskId
), Guid
);
558 DPRINT("DiskId: %s\n", Guid
);
559 DPRINT("StartingUsableOffset: %I64x\n", DriveLayout
->Gpt
.StartingUsableOffset
.QuadPart
);
560 DPRINT("UsableLength: %I64x\n", DriveLayout
->Gpt
.UsableLength
.QuadPart
);
561 DPRINT("MaxPartitionCount: %ld\n", DriveLayout
->Gpt
.MaxPartitionCount
);
562 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
564 FstubDbgPrintPartitionEx(DriveLayout
->PartitionEntry
, i
);
569 DPRINT("Unsupported partition style: %ld\n", DriveLayout
->PartitionStyle
);
575 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry
,
576 IN ULONG PartitionNumber
)
581 DPRINT("Printing partition %ld\n", PartitionNumber
);
583 switch (PartitionEntry
[PartitionNumber
].PartitionStyle
)
585 case PARTITION_STYLE_MBR
:
586 DPRINT(" StartingOffset: %I64x\n", PartitionEntry
[PartitionNumber
].StartingOffset
.QuadPart
);
587 DPRINT(" PartitionLength: %I64x\n", PartitionEntry
[PartitionNumber
].PartitionLength
.QuadPart
);
588 DPRINT(" RewritePartition: %d\n", PartitionEntry
[PartitionNumber
].RewritePartition
);
589 DPRINT(" PartitionType: %02x\n", PartitionEntry
[PartitionNumber
].Mbr
.PartitionType
);
590 DPRINT(" BootIndicator: %d\n", PartitionEntry
[PartitionNumber
].Mbr
.BootIndicator
);
591 DPRINT(" RecognizedPartition: %d\n", PartitionEntry
[PartitionNumber
].Mbr
.RecognizedPartition
);
592 DPRINT(" HiddenSectors: %ld\n", PartitionEntry
[PartitionNumber
].Mbr
.HiddenSectors
);
595 case PARTITION_STYLE_GPT
:
596 DPRINT(" StartingOffset: %I64x\n", PartitionEntry
[PartitionNumber
].StartingOffset
.QuadPart
);
597 DPRINT(" PartitionLength: %I64x\n", PartitionEntry
[PartitionNumber
].PartitionLength
.QuadPart
);
598 DPRINT(" RewritePartition: %d\n", PartitionEntry
[PartitionNumber
].RewritePartition
);
599 FstubDbgGuidToString(&(PartitionEntry
[PartitionNumber
].Gpt
.PartitionType
), Guid
);
600 DPRINT(" PartitionType: %s\n", Guid
);
601 FstubDbgGuidToString(&(PartitionEntry
[PartitionNumber
].Gpt
.PartitionId
), Guid
);
602 DPRINT(" PartitionId: %s\n", Guid
);
603 DPRINT(" Attributes: %16x\n", PartitionEntry
[PartitionNumber
].Gpt
.Attributes
);
604 DPRINT(" Name: %ws\n", PartitionEntry
[PartitionNumber
].Gpt
.Name
);
608 DPRINT(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
614 FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry
,
615 IN ULONG PartitionNumber
)
620 DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry
);
621 DPRINT("Modifying partition %ld\n", PartitionNumber
);
622 switch (PartitionEntry
->PartitionStyle
)
624 case PARTITION_STYLE_MBR
:
625 DPRINT(" PartitionType: %02x\n", PartitionEntry
->Mbr
.PartitionType
);
628 case PARTITION_STYLE_GPT
:
629 FstubDbgGuidToString(&(PartitionEntry
->Gpt
.PartitionType
), Guid
);
630 DPRINT(" PartitionType: %s\n", Guid
);
631 FstubDbgGuidToString(&(PartitionEntry
->Gpt
.PartitionId
), Guid
);
632 DPRINT(" PartitionId: %s\n", Guid
);
633 DPRINT(" Attributes: %16x\n", PartitionEntry
->Gpt
.Attributes
);
634 DPRINT(" Name: %ws\n", PartitionEntry
->Gpt
.Name
);
638 DPRINT(" Unsupported partition style: %ld\n", PartitionEntry
[PartitionNumber
].PartitionStyle
);
644 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk
,
645 IN PARTITION_STYLE
* PartitionStyle
)
648 PPARTITION_DESCRIPTOR PartitionDescriptor
;
651 ASSERT(IS_VALID_DISK_INFO(Disk
));
652 ASSERT(PartitionStyle
);
654 /* Read disk first sector */
655 Status
= FstubReadSector(Disk
->DeviceObject
,
659 if (!NT_SUCCESS(Status
))
664 /* Get the partition descriptor array */
665 PartitionDescriptor
= (PPARTITION_DESCRIPTOR
)
666 &(Disk
->Buffer
[PARTITION_TABLE_OFFSET
]);
667 /* If we have not the 0xAA55 then it's raw partition */
668 if (Disk
->Buffer
[BOOT_SIGNATURE_OFFSET
] != BOOT_RECORD_SIGNATURE
)
670 *PartitionStyle
= PARTITION_STYLE_RAW
;
672 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
673 else if (PartitionDescriptor
[0].PartitionType
== EFI_PMBR_OSTYPE_EFI
&&
674 PartitionDescriptor
[1].PartitionType
== 0 &&
675 PartitionDescriptor
[2].PartitionType
== 0 &&
676 PartitionDescriptor
[3].PartitionType
== 0)
678 *PartitionStyle
= PARTITION_STYLE_GPT
;
680 /* Otherwise, partition table is in MBR */
683 *PartitionStyle
= PARTITION_STYLE_MBR
;
686 return STATUS_SUCCESS
;
691 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer
)
695 if (DiskBuffer
->Buffer
)
697 ExFreePoolWithTag(DiskBuffer
->Buffer
, TAG_FSTUB
);
699 ExFreePoolWithTag(DiskBuffer
, TAG_FSTUB
);
705 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject
,
706 OUT PDISK_GEOMETRY_EX Geometry
)
710 PKEVENT Event
= NULL
;
711 PDISK_GEOMETRY_EX DiskGeometry
= NULL
;
712 PIO_STATUS_BLOCK IoStatusBlock
= NULL
;
715 ASSERT(DeviceObject
);
718 /* Allocate needed components */
719 DiskGeometry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(DISK_GEOMETRY_EX
), TAG_FSTUB
);
722 Status
= STATUS_INSUFFICIENT_RESOURCES
;
726 IoStatusBlock
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), TAG_FSTUB
);
729 Status
= STATUS_INSUFFICIENT_RESOURCES
;
733 Event
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
), TAG_FSTUB
);
736 Status
= STATUS_INSUFFICIENT_RESOURCES
;
739 /* Initialize the waiting event */
740 KeInitializeEvent(Event
, NotificationEvent
, FALSE
);
742 /* Build the request to get disk geometry */
743 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
,
748 sizeof(DISK_GEOMETRY_EX
),
754 Status
= STATUS_INSUFFICIENT_RESOURCES
;
758 /* Call the driver and wait for completion if needed */
759 Status
= IoCallDriver(DeviceObject
, Irp
);
760 if (Status
== STATUS_PENDING
)
762 KeWaitForSingleObject(Event
, Executive
, KernelMode
, FALSE
, NULL
);
763 Status
= IoStatusBlock
->Status
;
766 /* In case of a success, return read data */
767 if (NT_SUCCESS(Status
))
769 *Geometry
= *DiskGeometry
;
775 ExFreePoolWithTag(DiskGeometry
, TAG_FSTUB
);
777 if (NT_SUCCESS(Status
))
779 ASSERT(Geometry
->Geometry
.BytesPerSector
% PARTITION_ENTRY_SIZE
== 0);
785 ExFreePoolWithTag(IoStatusBlock
, TAG_FSTUB
);
790 ExFreePoolWithTag(Event
, TAG_FSTUB
);
798 FstubReadHeaderEFI(IN PDISK_INFORMATION Disk
,
799 IN BOOLEAN ReadBackupTable
,
800 PEFI_PARTITION_HEADER HeaderBuffer
)
803 PUCHAR Sector
= NULL
;
804 ULONGLONG StartingSector
;
805 PEFI_PARTITION_HEADER EFIHeader
;
806 ULONG i
, HeaderCRC32
, PreviousCRC32
, SectoredPartitionEntriesSize
, LonelyPartitions
;
810 ASSERT(IS_VALID_DISK_INFO(Disk
));
811 ASSERT(HeaderBuffer
);
813 /* In case we want to read backup table, we read last disk sector */
816 StartingSector
= Disk
->SectorCount
- 1ULL;
820 /* Otherwise we start at first sector (as sector 0 is the MBR) */
821 StartingSector
= 1ULL;
824 Status
= FstubReadSector(Disk
->DeviceObject
,
828 if (!NT_SUCCESS(Status
))
830 DPRINT("EFI::Failed reading header!\n");
833 /* Let's use read buffer as EFI_PARTITION_HEADER */
834 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
837 /* First check signature
838 * Then, check version (we only support v1)
839 * Finally check header size
841 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
842 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
843 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
845 DPRINT("EFI::Wrong signature/version/header size!\n");
846 DPRINT("%I64x (expected: %I64x)\n", EFIHeader
->Signature
, EFI_HEADER_SIGNATURE
);
847 DPRINT("%03x (expected: %03x)\n", EFIHeader
->Revision
, EFI_HEADER_REVISION_1
);
848 DPRINT("%02x (expected: %02x)\n", EFIHeader
->HeaderSize
, sizeof(EFI_PARTITION_HEADER
));
849 return STATUS_DISK_CORRUPT_ERROR
;
852 /* Save current checksum */
853 HeaderCRC32
= EFIHeader
->HeaderCRC32
;
854 /* Then zero the one in EFI header. This is needed to compute header checksum */
855 EFIHeader
->HeaderCRC32
= 0;
856 /* Compute header checksum and compare with the one present in partition table */
857 if (RtlComputeCrc32(0, (PUCHAR
)Disk
->Buffer
, sizeof(EFI_PARTITION_HEADER
)) != HeaderCRC32
)
859 DPRINT("EFI::Not matching header checksum!\n");
860 return STATUS_DISK_CORRUPT_ERROR
;
862 /* Put back removed checksum in header */
863 EFIHeader
->HeaderCRC32
= HeaderCRC32
;
865 /* Check if current LBA is matching with ours */
866 if (EFIHeader
->MyLBA
!= StartingSector
)
868 DPRINT("EFI::Not matching starting sector!\n");
869 return STATUS_DISK_CORRUPT_ERROR
;
872 /* Allocate a buffer to read a sector on the disk */
873 Sector
= ExAllocatePoolWithTag(NonPagedPool
,
878 DPRINT("EFI::Lacking resources!\n");
879 return STATUS_INSUFFICIENT_RESOURCES
;
882 /* Count how much sectors we'll have to read to read the whole partition table */
883 SectoredPartitionEntriesSize
= (EFIHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
884 /* Compute partition table checksum */
885 for (i
= 0, PreviousCRC32
= 0; i
< SectoredPartitionEntriesSize
; i
++)
887 Status
= FstubReadSector(Disk
->DeviceObject
,
889 EFIHeader
->PartitionEntryLBA
+ i
,
891 if (!NT_SUCCESS(Status
))
893 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
894 DPRINT("EFI::Failed reading sector for partition entry!\n");
898 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
, Disk
->SectorSize
);
901 /* Check whether we have a last sector not full of partitions */
902 LonelyPartitions
= (EFIHeader
->NumberOfEntries
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
;
903 /* In such case, we have to complete checksum computation */
904 if (LonelyPartitions
!= 0)
906 /* Read the sector that contains those partitions */
907 Status
= FstubReadSector(Disk
->DeviceObject
,
909 EFIHeader
->PartitionEntryLBA
+ i
,
911 if (!NT_SUCCESS(Status
))
913 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
914 DPRINT("EFI::Failed reading sector for partition entry!\n");
918 /* Then complete checksum by computing on each partition */
919 for (i
= 0; i
< LonelyPartitions
; i
++)
921 PreviousCRC32
= RtlComputeCrc32(PreviousCRC32
, Sector
+ i
* PARTITION_ENTRY_SIZE
, PARTITION_ENTRY_SIZE
);
925 /* Finally, release memory */
926 ExFreePoolWithTag(Sector
, TAG_FSTUB
);
928 /* Compare checksums */
929 if (PreviousCRC32
== EFIHeader
->PartitionEntryCRC32
)
931 /* In case of a success, return read header */
932 *HeaderBuffer
= *EFIHeader
;
933 return STATUS_SUCCESS
;
937 DPRINT("EFI::Not matching partition table checksum!\n");
938 DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader
->PartitionEntryCRC32
, PreviousCRC32
);
939 return STATUS_DISK_CORRUPT_ERROR
;
945 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk
,
946 IN BOOLEAN ReadBackupTable
,
947 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
950 EFI_PARTITION_HEADER EfiHeader
;
951 ULONGLONG SectorsForPartitions
;
952 EFI_PARTITION_ENTRY PartitionEntry
;
953 BOOLEAN UpdatedPartitionTable
= FALSE
;
954 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
955 ULONG i
, PartitionCount
, PartitionIndex
, PartitionsPerSector
;
963 /* Read EFI header */
964 Status
= FstubReadHeaderEFI(Disk
,
967 if (!NT_SUCCESS(Status
))
972 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
973 DriveLayoutEx
= ExAllocatePoolWithTag(NonPagedPool
,
974 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX
, PartitionEntry
) +
975 EfiHeader
.NumberOfEntries
* sizeof(PARTITION_INFORMATION_EX
),
979 return STATUS_INSUFFICIENT_RESOURCES
;
984 /* If we read backup but if it doesn't match with current geometry */
985 if ((Disk
->SectorCount
- 1ULL) != EfiHeader
.AlternateLBA
)
987 /* We'll update it. First, count number of sectors needed to store partitions */
988 SectorsForPartitions
= ((ULONGLONG
)EfiHeader
.NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
989 /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
990 EfiHeader
.FirstUsableLBA
= SectorsForPartitions
+ 2;
991 /* Then set last usable LBA: Last sector - GPT header - Partitions entries */
992 EfiHeader
.LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
993 /* Inform that we'll rewrite partition table */
994 UpdatedPartitionTable
= TRUE
;
998 DriveLayoutEx
->PartitionStyle
= PARTITION_STYLE_GPT
;
999 /* Translate LBA -> Offset */
1000 DriveLayoutEx
->Gpt
.StartingUsableOffset
.QuadPart
= EfiHeader
.FirstUsableLBA
* Disk
->SectorSize
;
1001 DriveLayoutEx
->Gpt
.UsableLength
.QuadPart
= EfiHeader
.LastUsableLBA
- EfiHeader
.FirstUsableLBA
* Disk
->SectorSize
;
1002 DriveLayoutEx
->Gpt
.MaxPartitionCount
= EfiHeader
.NumberOfEntries
;
1003 DriveLayoutEx
->Gpt
.DiskId
= EfiHeader
.DiskGUID
;
1005 /* Count number of partitions per sector */
1006 PartitionsPerSector
= (Disk
->SectorSize
/ PARTITION_ENTRY_SIZE
);
1007 /* Read all partitions and fill in structure */
1008 for (i
= 0, PartitionCount
= 0, PartitionIndex
= PartitionsPerSector
;
1009 i
< EfiHeader
.NumberOfEntries
;
1012 /* Only read following sector if we finished with previous sector */
1013 if (PartitionIndex
== PartitionsPerSector
)
1015 Status
= FstubReadSector(Disk
->DeviceObject
,
1017 EfiHeader
.PartitionEntryLBA
+ (i
/ PartitionsPerSector
),
1019 if (!NT_SUCCESS(Status
))
1021 ExFreePoolWithTag(DriveLayoutEx
, TAG_FSTUB
);
1027 /* Read following partition */
1028 PartitionEntry
= ((PEFI_PARTITION_ENTRY
)Disk
->Buffer
)[PartitionIndex
];
1031 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1032 if (PartitionEntry
.PartitionType
.Data1
== 0 &&
1033 PartitionEntry
.PartitionType
.Data2
== 0 &&
1034 PartitionEntry
.PartitionType
.Data3
== 0 &&
1035 ((PULONGLONG
)PartitionEntry
.PartitionType
.Data4
)[0] == 0)
1040 /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */
1041 DriveLayoutEx
->PartitionEntry
[PartitionCount
].StartingOffset
.QuadPart
= PartitionEntry
.StartingLBA
* Disk
->SectorSize
;
1042 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionLength
.QuadPart
= (PartitionEntry
.EndingLBA
-
1043 PartitionEntry
.StartingLBA
+ 1) *
1045 /* This number starts from 1 */
1046 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionNumber
= PartitionCount
+ 1;
1047 DriveLayoutEx
->PartitionEntry
[PartitionCount
].RewritePartition
= FALSE
;
1048 DriveLayoutEx
->PartitionEntry
[PartitionCount
].PartitionStyle
= PARTITION_STYLE_GPT
;
1049 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionType
= PartitionEntry
.PartitionType
;
1050 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.PartitionId
= PartitionEntry
.UniquePartition
;
1051 DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Attributes
= PartitionEntry
.Attributes
;
1052 RtlCopyMemory(DriveLayoutEx
->PartitionEntry
[PartitionCount
].Gpt
.Name
,
1053 PartitionEntry
.Name
, sizeof(PartitionEntry
.Name
));
1055 /* Update partition count */
1058 DriveLayoutEx
->PartitionCount
= PartitionCount
;
1060 /* If we updated partition table using backup table, rewrite partition table */
1061 if (UpdatedPartitionTable
)
1063 IoWritePartitionTableEx(Disk
->DeviceObject
,
1067 /* Finally, return read data */
1068 *DriveLayout
= DriveLayoutEx
;
1075 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk
,
1076 IN BOOLEAN ReturnRecognizedPartitions
,
1077 OUT
struct _DRIVE_LAYOUT_INFORMATION_EX
** ReturnedDriveLayout
)
1081 PDRIVE_LAYOUT_INFORMATION DriveLayout
= NULL
;
1082 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx
= NULL
;
1085 ASSERT(IS_VALID_DISK_INFO(Disk
));
1086 ASSERT(ReturnedDriveLayout
);
1089 *ReturnedDriveLayout
= NULL
;
1091 /* Read partition table the old way */
1092 Status
= IoReadPartitionTable(Disk
->DeviceObject
,
1094 ReturnRecognizedPartitions
,
1096 if (!NT_SUCCESS(Status
))
1101 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
1102 DriveLayoutEx
= ExAllocatePoolWithTag(NonPagedPool
,
1103 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX
, PartitionEntry
) +
1104 DriveLayout
->PartitionCount
* sizeof(PARTITION_INFORMATION_EX
),
1108 /* Let's not leak memory as in Windows 2003 */
1109 ExFreePool(DriveLayout
);
1110 return STATUS_INSUFFICIENT_RESOURCES
;
1113 /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
1114 DriveLayoutEx
->PartitionStyle
= PARTITION_STYLE_MBR
;
1115 DriveLayoutEx
->PartitionCount
= DriveLayout
->PartitionCount
;
1116 DriveLayoutEx
->Mbr
.Signature
= DriveLayout
->Signature
;
1118 /* Convert each found partition */
1119 for (i
= 0; i
< DriveLayout
->PartitionCount
; i
++)
1121 DriveLayoutEx
->PartitionEntry
[i
].PartitionStyle
= PARTITION_STYLE_MBR
;
1122 DriveLayoutEx
->PartitionEntry
[i
].StartingOffset
= DriveLayout
->PartitionEntry
[i
].StartingOffset
;
1123 DriveLayoutEx
->PartitionEntry
[i
].PartitionLength
= DriveLayout
->PartitionEntry
[i
].PartitionLength
;
1124 DriveLayoutEx
->PartitionEntry
[i
].PartitionNumber
= DriveLayout
->PartitionEntry
[i
].PartitionNumber
;
1125 DriveLayoutEx
->PartitionEntry
[i
].RewritePartition
= DriveLayout
->PartitionEntry
[i
].RewritePartition
;
1126 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.PartitionType
= DriveLayout
->PartitionEntry
[i
].PartitionType
;
1127 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.BootIndicator
= DriveLayout
->PartitionEntry
[i
].BootIndicator
;
1128 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.RecognizedPartition
= DriveLayout
->PartitionEntry
[i
].RecognizedPartition
;
1129 DriveLayoutEx
->PartitionEntry
[i
].Mbr
.HiddenSectors
= DriveLayout
->PartitionEntry
[i
].HiddenSectors
;
1132 /* Finally, return data and free old structure */
1133 *ReturnedDriveLayout
= DriveLayoutEx
;
1134 ExFreePool(DriveLayout
);
1136 return STATUS_SUCCESS
;
1141 FstubReadSector(IN PDEVICE_OBJECT DeviceObject
,
1142 IN ULONG SectorSize
,
1143 IN ULONGLONG StartingSector OPTIONAL
,
1149 LARGE_INTEGER StartingOffset
;
1150 IO_STATUS_BLOCK IoStatusBlock
;
1151 PIO_STACK_LOCATION IoStackLocation
;
1154 ASSERT(DeviceObject
);
1158 /* Compute starting offset */
1159 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1161 /* Initialize waiting event */
1162 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1165 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
,
1174 return STATUS_INSUFFICIENT_RESOURCES
;
1177 /* Override volume verify */
1178 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1179 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1181 /* Then call driver, and wait for completion if needed */
1182 Status
= IoCallDriver(DeviceObject
, Irp
);
1183 if (Status
== STATUS_PENDING
)
1185 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1186 Status
= IoStatusBlock
.Status
;
1194 FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk
,
1195 IN ULONG PartitionNumber
,
1196 IN SET_PARTITION_INFORMATION_GPT
* PartitionInfo
)
1199 PDRIVE_LAYOUT_INFORMATION_EX Layout
= NULL
;
1203 ASSERT(PartitionInfo
);
1205 /* Partition 0 isn't correct (should start at 1) */
1206 if (PartitionNumber
== 0)
1208 return STATUS_INVALID_PARAMETER
;
1211 /* Read partition table */
1212 Status
= IoReadPartitionTableEx(Disk
->DeviceObject
, &Layout
);
1213 if (!NT_SUCCESS(Status
))
1219 /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */
1220 if (Layout
->PartitionCount
<= --PartitionNumber
)
1223 return STATUS_INVALID_PARAMETER
;
1226 /* Erase actual partition entry data with provided ones */
1227 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionType
= PartitionInfo
->PartitionType
;
1228 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.PartitionId
= PartitionInfo
->PartitionId
;
1229 Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Attributes
= PartitionInfo
->Attributes
;
1230 RtlCopyMemory(Layout
->PartitionEntry
[PartitionNumber
].Gpt
.Name
, PartitionInfo
->Name
, sizeof(PartitionInfo
->Name
));
1232 /* Rewrite the whole partition table to update the modified entry */
1233 Status
= IoWritePartitionTableEx(Disk
->DeviceObject
, Layout
);
1235 /* Free partition table and return */
1242 FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk
,
1243 IN BOOLEAN FixErrors
)
1246 PEFI_PARTITION_HEADER EFIHeader
;
1247 EFI_PARTITION_HEADER ReadEFIHeader
;
1248 BOOLEAN PrimaryValid
= FALSE
, BackupValid
= FALSE
;
1251 EFIHeader
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(EFI_PARTITION_HEADER
), TAG_FSTUB
);
1254 return STATUS_INSUFFICIENT_RESOURCES
;
1257 Status
= FstubReadHeaderEFI(Disk
, FALSE
, &ReadEFIHeader
);
1258 if (NT_SUCCESS(Status
))
1260 PrimaryValid
= TRUE
;
1263 Status
= FstubReadHeaderEFI(Disk
, TRUE
, &ReadEFIHeader
);
1264 if (NT_SUCCESS(Status
))
1271 if (!BackupValid
|| !FixErrors
)
1273 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1274 return STATUS_DISK_CORRUPT_ERROR
;
1277 DPRINT1("EFI::Partition table fixing not yet supported!\n");
1278 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1279 return STATUS_NOT_IMPLEMENTED
;
1281 else if (!BackupValid
)
1283 if (!PrimaryValid
|| !FixErrors
)
1285 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1286 return STATUS_DISK_CORRUPT_ERROR
;
1289 DPRINT1("EFI::Partition table fixing not yet supported!\n");
1290 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1291 return STATUS_NOT_IMPLEMENTED
;
1295 ExFreePoolWithTag(EFIHeader
, TAG_FSTUB
);
1296 return STATUS_SUCCESS
;
1302 FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
)
1305 ULONG Signature
= 0;
1306 PMASTER_BOOT_RECORD MasterBootRecord
;
1310 ASSERT(IS_VALID_DISK_INFO(Disk
));
1312 /* Read if a MBR is already present */
1313 Status
= FstubReadSector(Disk
->DeviceObject
,
1317 MasterBootRecord
= (PMASTER_BOOT_RECORD
)Disk
->Buffer
;
1318 /* If one has been found */
1319 if (NT_SUCCESS(Status
) && MasterBootRecord
->MasterBootRecordMagic
== BOOT_RECORD_SIGNATURE
)
1321 /* Save its signature */
1322 Signature
= MasterBootRecord
->Signature
;
1326 RtlZeroMemory(MasterBootRecord
, Disk
->SectorSize
);
1327 /* Then create a fake MBR matching those purposes:
1328 * It must have only partition. Type of this partition
1329 * has to be 0xEE to signal a GPT is following.
1330 * This partition has to cover the whole disk. To prevent
1331 * any disk modification by a program that wouldn't
1332 * understand anything to GPT.
1334 MasterBootRecord
->Signature
= Signature
;
1335 MasterBootRecord
->PartitionTable
[0].StartSector
= 2;
1336 MasterBootRecord
->PartitionTable
[0].SystemIndicator
= EFI_PMBR_OSTYPE_EFI
;
1337 MasterBootRecord
->PartitionTable
[0].EndHead
= 0xFF;
1338 MasterBootRecord
->PartitionTable
[0].EndSector
= 0xFF;
1339 MasterBootRecord
->PartitionTable
[0].EndCylinder
= 0xFF;
1340 MasterBootRecord
->PartitionTable
[0].SectorCountBeforePartition
= 1;
1341 MasterBootRecord
->PartitionTable
[0].PartitionSectorCount
= 0xFFFFFFFF;
1342 MasterBootRecord
->MasterBootRecordMagic
= BOOT_RECORD_SIGNATURE
;
1344 /* Finally, write that MBR */
1345 return FstubWriteSector(Disk
->DeviceObject
,
1353 FstubWriteEntryEFI(IN PDISK_INFORMATION Disk
,
1354 IN ULONG PartitionsSizeSector
,
1355 IN ULONG PartitionEntryNumber
,
1356 IN PEFI_PARTITION_ENTRY PartitionEntry
,
1357 IN BOOLEAN WriteBackupTable
,
1358 IN BOOLEAN ForceWrite
,
1359 OUT PULONG PartitionEntryCRC32 OPTIONAL
)
1362 ULONGLONG FirstEntryLBA
;
1363 NTSTATUS Status
= STATUS_SUCCESS
;
1367 ASSERT(IS_VALID_DISK_INFO(Disk
));
1369 /* Get the first LBA where the partition table is:
1370 * On primary table, it's sector 2 (skip MBR & Header)
1371 * On backup table, it's ante last sector (Header) minus partition table size
1373 if (!WriteBackupTable
)
1375 FirstEntryLBA
= 2ULL;
1379 FirstEntryLBA
= Disk
->SectorCount
- PartitionsSizeSector
- 1;
1382 /* Copy the entry at the proper place into the buffer
1383 * That way, we don't erase previous entries
1385 RtlCopyMemory(Disk
->Buffer
+ (((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
) / sizeof(PUSHORT
)),
1387 sizeof(EFI_PARTITION_ENTRY
));
1388 /* Compute size of buffer */
1389 Offset
= (PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) % Disk
->SectorSize
+ PARTITION_ENTRY_SIZE
;
1390 ASSERT(Offset
<= Disk
->SectorSize
);
1392 /* If it's full of partition entries, or if call ask for it, write down the data */
1393 if (Offset
== Disk
->SectorSize
|| ForceWrite
)
1395 /* We will write at first entry LBA + a shift made by already present/written entries */
1396 Status
= FstubWriteSector(Disk
->DeviceObject
,
1398 FirstEntryLBA
+ ((PartitionEntryNumber
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
),
1400 if (!NT_SUCCESS(Status
))
1404 /* We clean buffer */
1405 RtlZeroMemory(Disk
->Buffer
, Disk
->SectorSize
);
1408 /* If we have a buffer for CRC32, then compute it */
1409 if (PartitionEntryCRC32
)
1411 *PartitionEntryCRC32
= RtlComputeCrc32(*PartitionEntryCRC32
, (PUCHAR
)PartitionEntry
, PARTITION_ENTRY_SIZE
);
1419 FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk
,
1420 IN ULONG PartitionsSizeSector
,
1422 IN ULONG NumberOfEntries
,
1423 IN ULONGLONG FirstUsableLBA
,
1424 IN ULONGLONG LastUsableLBA
,
1425 IN ULONG PartitionEntryCRC32
,
1426 IN BOOLEAN WriteBackupTable
)
1428 PEFI_PARTITION_HEADER EFIHeader
;
1432 ASSERT(IS_VALID_DISK_INFO(Disk
));
1434 /* Let's use read buffer as EFI_PARTITION_HEADER */
1435 EFIHeader
= (PEFI_PARTITION_HEADER
)Disk
->Buffer
;
1437 /* Complete standard header information */
1438 EFIHeader
->Signature
= EFI_HEADER_SIGNATURE
;
1439 EFIHeader
->Revision
= EFI_HEADER_REVISION_1
;
1440 EFIHeader
->HeaderSize
= sizeof(EFI_PARTITION_HEADER
);
1441 /* Set no CRC32 checksum at the moment */
1442 EFIHeader
->HeaderCRC32
= 0;
1443 EFIHeader
->Reserved
= 0;
1444 /* Check whether we're writing primary or backup
1445 * That way, we can ajust LBA setting:
1446 * Primary is on first sector
1447 * Backup is on last sector
1449 if (!WriteBackupTable
)
1451 EFIHeader
->MyLBA
= 1ULL;
1452 EFIHeader
->AlternateLBA
= Disk
->SectorCount
- 1ULL;
1456 EFIHeader
->MyLBA
= Disk
->SectorCount
- 1ULL;
1457 EFIHeader
->AlternateLBA
= 1ULL;
1459 /* Fill in with received data */
1460 EFIHeader
->FirstUsableLBA
= FirstUsableLBA
;
1461 EFIHeader
->LastUsableLBA
= LastUsableLBA
;
1462 EFIHeader
->DiskGUID
= DiskGUID
;
1463 /* Check whether we're writing primary or backup
1464 * That way, we can ajust LBA setting:
1465 * On primary, partition entries are just after header, so sector 2
1466 * On backup, partition entries are just before header, so, last sector minus partition table size
1468 if (!WriteBackupTable
)
1470 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
+ 1ULL;
1474 EFIHeader
->PartitionEntryLBA
= EFIHeader
->MyLBA
- PartitionsSizeSector
;
1476 /* Complete filling in */
1477 EFIHeader
->NumberOfEntries
= NumberOfEntries
;
1478 EFIHeader
->SizeOfPartitionEntry
= PARTITION_ENTRY_SIZE
;
1479 EFIHeader
->PartitionEntryCRC32
= PartitionEntryCRC32
;
1480 /* Finally, compute header checksum */
1481 EFIHeader
->HeaderCRC32
= RtlComputeCrc32(0, (PUCHAR
)EFIHeader
, sizeof(EFI_PARTITION_HEADER
));
1483 /* Debug the way we'll break disk, to let user pray */
1484 DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable
? "backup" : "primary"));
1485 DPRINT(" Signature: %I64x\n Revision: %x\n HeaderSize: %x\n HeaderCRC32: %x\n",
1486 EFIHeader
->Signature
, EFIHeader
->Revision
, EFIHeader
->HeaderSize
, EFIHeader
->HeaderCRC32
);
1487 DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n LastUsableLBA: %I64x\n",
1488 EFIHeader
->MyLBA
, EFIHeader
->AlternateLBA
, EFIHeader
->FirstUsableLBA
, EFIHeader
->LastUsableLBA
);
1489 DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry: %x\n PartitionEntryCRC32: %x\n",
1490 EFIHeader
->PartitionEntryLBA
, EFIHeader
->NumberOfEntries
,
1491 EFIHeader
->SizeOfPartitionEntry
, EFIHeader
->PartitionEntryCRC32
);
1493 /* Write header to disk */
1494 return FstubWriteSector(Disk
->DeviceObject
,
1502 FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk
,
1504 IN ULONG MaxPartitionCount
,
1505 IN ULONGLONG FirstUsableLBA
,
1506 IN ULONGLONG LastUsableLBA
,
1507 IN BOOLEAN WriteBackupTable
,
1508 IN ULONG PartitionCount
,
1509 IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL
)
1512 EFI_PARTITION_ENTRY Entry
;
1513 ULONG i
, WrittenPartitions
, SectoredPartitionEntriesSize
, PartitionEntryCRC32
;
1517 ASSERT(MaxPartitionCount
>= 128);
1518 ASSERT(PartitionCount
<= MaxPartitionCount
);
1520 PartitionEntryCRC32
= 0;
1521 /* Count how much sectors we'll have to read to read the whole partition table */
1522 SectoredPartitionEntriesSize
= (MaxPartitionCount
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
1524 for (i
= 0, WrittenPartitions
= 0; i
< PartitionCount
; i
++)
1526 /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */
1527 if (PartitionEntries
[i
].Gpt
.PartitionType
.Data1
== 0 &&
1528 PartitionEntries
[i
].Gpt
.PartitionType
.Data2
== 0 &&
1529 PartitionEntries
[i
].Gpt
.PartitionType
.Data3
== 0 &&
1530 ((PULONGLONG
)PartitionEntries
[i
].Gpt
.PartitionType
.Data4
)[0] == 0)
1535 /* Copy the entry in the partition entry format */
1536 FstubCopyEntryEFI(&Entry
, &PartitionEntries
[i
], Disk
->SectorSize
);
1537 /* Then write the entry to the disk */
1538 Status
= FstubWriteEntryEFI(Disk
,
1539 SectoredPartitionEntriesSize
,
1544 &PartitionEntryCRC32
);
1545 if (!NT_SUCCESS(Status
))
1549 WrittenPartitions
++;
1552 /* Zero the buffer to write zeros to the disk */
1553 RtlZeroMemory(&Entry
, sizeof(EFI_PARTITION_ENTRY
));
1554 /* Write the disks with zeros for every unused remaining partition entry */
1555 for (i
= WrittenPartitions
; i
< MaxPartitionCount
; i
++)
1557 Status
= FstubWriteEntryEFI(Disk
,
1558 SectoredPartitionEntriesSize
,
1563 &PartitionEntryCRC32
);
1564 if (!NT_SUCCESS(Status
))
1570 /* Once we're done, write the GPT header */
1571 return FstubWriteHeaderEFI(Disk
,
1572 SectoredPartitionEntriesSize
,
1577 PartitionEntryCRC32
,
1583 FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk
,
1584 IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
)
1587 PDRIVE_LAYOUT_INFORMATION DriveLayout
;
1590 ASSERT(IS_VALID_DISK_INFO(Disk
));
1593 /* Convert data to the correct format */
1594 DriveLayout
= FstubConvertExtendedToLayout(LayoutEx
);
1597 return STATUS_INSUFFICIENT_RESOURCES
;
1600 /* Really write information */
1601 Status
= IoWritePartitionTable(Disk
->DeviceObject
,
1603 Disk
->DiskGeometry
.Geometry
.SectorsPerTrack
,
1604 Disk
->DiskGeometry
.Geometry
.TracksPerCylinder
,
1607 /* Free allocated structure and return */
1608 ExFreePoolWithTag(DriveLayout
, TAG_FSTUB
);
1614 FstubWriteSector(IN PDEVICE_OBJECT DeviceObject
,
1615 IN ULONG SectorSize
,
1616 IN ULONGLONG StartingSector OPTIONAL
,
1622 LARGE_INTEGER StartingOffset
;
1623 IO_STATUS_BLOCK IoStatusBlock
;
1624 PIO_STACK_LOCATION IoStackLocation
;
1627 ASSERT(DeviceObject
);
1631 /* Compute starting offset */
1632 StartingOffset
.QuadPart
= StartingSector
* SectorSize
;
1634 /* Initialize waiting event */
1635 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1638 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_WRITE
,
1647 return STATUS_INSUFFICIENT_RESOURCES
;
1650 /* Override volume verify */
1651 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
1652 IoStackLocation
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1654 /* Then call driver, and wait for completion if needed */
1655 Status
= IoCallDriver(DeviceObject
, Irp
);
1656 if (Status
== STATUS_PENDING
)
1658 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1659 Status
= IoStatusBlock
.Status
;
1665 /* FUNCTIONS *****************************************************************/
1672 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject
,
1673 IN
struct _CREATE_DISK
* Disk
)
1675 PARTITION_STYLE PartitionStyle
;
1678 ASSERT(DeviceObject
);
1680 /* Get partition style. If caller didn't provided data, assume it's raw */
1681 PartitionStyle
= ((Disk
) ? Disk
->PartitionStyle
: PARTITION_STYLE_RAW
);
1682 /* Then, call appropriate internal function */
1683 switch (PartitionStyle
)
1685 case PARTITION_STYLE_MBR
:
1686 return FstubCreateDiskMBR(DeviceObject
, &(Disk
->Mbr
));
1687 case PARTITION_STYLE_GPT
:
1688 return FstubCreateDiskEFI(DeviceObject
, &(Disk
->Gpt
));
1689 case PARTITION_STYLE_RAW
:
1690 return FstubCreateDiskRaw(DeviceObject
);
1692 return STATUS_NOT_SUPPORTED
;
1701 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation
,
1706 PLIST_ENTRY NextEntry
;
1707 PFILE_OBJECT FileObject
;
1708 DISK_GEOMETRY DiskGeometry
;
1709 PDEVICE_OBJECT DeviceObject
;
1710 UNICODE_STRING DeviceStringW
;
1711 IO_STATUS_BLOCK IoStatusBlock
;
1712 CHAR Buffer
[128], ArcBuffer
[128];
1713 NTSTATUS Status
= STATUS_SUCCESS
;
1714 BOOLEAN SingleDisk
, IsBootDiskInfoEx
;
1715 PARC_DISK_SIGNATURE ArcDiskSignature
;
1716 PARC_DISK_INFORMATION ArcDiskInformation
;
1717 PARTITION_INFORMATION_EX PartitionInformation
;
1718 PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
= NULL
;
1719 ULONG DiskCount
, DiskNumber
, Signature
, PartitionNumber
;
1720 ANSI_STRING ArcBootString
, ArcSystemString
, DeviceStringA
, ArcNameStringA
;
1721 extern PLOADER_PARAMETER_BLOCK IopLoaderBlock
;
1724 /* Get loader block. If it's null, we come to late */
1725 if (!IopLoaderBlock
)
1727 return STATUS_TOO_LATE
;
1730 /* Check buffer size */
1731 if (Size
< sizeof(BOOTDISK_INFORMATION
))
1733 return STATUS_INVALID_PARAMETER
;
1736 /* Init some useful stuff:
1737 * Get arc disks information
1738 * Check whether we have a single disk
1739 * Check received structure size (extended or not?)
1740 * Init boot strings (system/boot)
1741 * Finaly, get disk count
1743 ArcDiskInformation
= IopLoaderBlock
->ArcDiskInformation
;
1744 SingleDisk
= IsListEmpty(&(ArcDiskInformation
->DiskSignatureListHead
));
1745 IsBootDiskInfoEx
= (Size
>= sizeof(BOOTDISK_INFORMATION_EX
));
1746 RtlInitAnsiString(&ArcBootString
, IopLoaderBlock
->ArcBootDeviceName
);
1747 RtlInitAnsiString(&ArcSystemString
, IopLoaderBlock
->ArcHalDeviceName
);
1748 DiskCount
= IoGetConfigurationInformation()->DiskCount
;
1750 /* If no disk, return success */
1753 return STATUS_SUCCESS
;
1756 /* Now, browse all disks */
1757 for (DiskNumber
= 0; DiskNumber
< DiskCount
; DiskNumber
++)
1759 /* Create the device name */
1760 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition0", DiskNumber
);
1761 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1762 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1763 if (!NT_SUCCESS(Status
))
1768 /* Get its device object */
1769 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1770 FILE_READ_ATTRIBUTES
,
1773 RtlFreeUnicodeString(&DeviceStringW
);
1774 if (!NT_SUCCESS(Status
))
1779 /* Prepare for getting disk geometry */
1780 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1785 sizeof(DISK_GEOMETRY
),
1791 ObDereferenceObject(FileObject
);
1795 /* Then, call the drive, and wait for it if needed */
1796 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1797 Status
= IoCallDriver(DeviceObject
, Irp
);
1798 if (Status
== STATUS_PENDING
)
1800 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1801 Status
= IoStatusBlock
.Status
;
1803 if (!NT_SUCCESS(Status
))
1805 ObDereferenceObject(FileObject
);
1809 /* Read partition table */
1810 Status
= IoReadPartitionTableEx(DeviceObject
,
1813 /* FileObject, you can go! */
1814 ObDereferenceObject(FileObject
);
1816 if (!NT_SUCCESS(Status
))
1821 /* Ensure we have at least 512 bytes per sector */
1822 if (DiskGeometry
.BytesPerSector
< 512)
1824 DiskGeometry
.BytesPerSector
= 512;
1827 /* Now, for each arc disk, try to find the matching */
1828 for (NextEntry
= ArcDiskInformation
->DiskSignatureListHead
.Flink
;
1829 NextEntry
!= &ArcDiskInformation
->DiskSignatureListHead
;
1830 NextEntry
= NextEntry
->Flink
)
1832 ArcDiskSignature
= CONTAINING_RECORD(NextEntry
,
1835 /* If they matches, ie
1836 * - There's only one disk for both BIOS and detected
1837 * - Signatures are matching
1839 * (We don't check checksums here)
1841 if (((SingleDisk
&& DiskCount
== 1) ||
1842 (IopVerifyDiskSignature(DriveLayout
, ArcDiskSignature
, &Signature
))) &&
1843 (DriveLayout
->PartitionStyle
== PARTITION_STYLE_MBR
))
1845 /* Create arc name */
1846 sprintf(ArcBuffer
, "\\ArcName\\%s", ArcDiskSignature
->ArcName
);
1847 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1849 /* Browse all partitions */
1850 for (PartitionNumber
= 1; PartitionNumber
<= DriveLayout
->PartitionCount
; PartitionNumber
++)
1852 /* Create its device name */
1853 sprintf(Buffer
, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber
, PartitionNumber
);
1854 RtlInitAnsiString(&DeviceStringA
, Buffer
);
1855 Status
= RtlAnsiStringToUnicodeString(&DeviceStringW
, &DeviceStringA
, TRUE
);
1856 if (!NT_SUCCESS(Status
))
1861 /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */
1864 Signature
= DriveLayout
->Mbr
.Signature
;
1867 /* Create partial arc name */
1868 sprintf(ArcBuffer
, "%spartition(%lu)", ArcDiskSignature
->ArcName
, PartitionNumber
);
1869 RtlInitAnsiString(&ArcNameStringA
, ArcBuffer
);
1871 /* If it's matching boot string */
1872 if (RtlEqualString(&ArcNameStringA
, &ArcBootString
, TRUE
))
1874 /* Then, fill in information about boot device */
1875 BootDiskInformation
->BootDeviceSignature
= Signature
;
1877 /* Get its device object */
1878 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1879 FILE_READ_ATTRIBUTES
,
1882 if (!NT_SUCCESS(Status
))
1884 RtlFreeUnicodeString(&DeviceStringW
);
1888 /* And call the drive to get information about partition */
1889 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
1893 &PartitionInformation
,
1894 sizeof(PARTITION_INFORMATION_EX
),
1900 ObDereferenceObject(FileObject
);
1901 RtlFreeUnicodeString(&DeviceStringW
);
1905 /* Call & wait if needed */
1906 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1907 Status
= IoCallDriver(DeviceObject
, Irp
);
1908 if (Status
== STATUS_PENDING
)
1910 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1911 Status
= IoStatusBlock
.Status
;
1913 if (!NT_SUCCESS(Status
))
1915 ObDereferenceObject(FileObject
);
1916 RtlFreeUnicodeString(&DeviceStringW
);
1920 /* We get partition offset as demanded and return it */
1921 BootDiskInformation
->BootPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
1923 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
1924 if (IsBootDiskInfoEx
)
1926 /* Is PT MBR or GPT? */
1927 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
1929 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
1930 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= TRUE
;
1934 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->BootDeviceIsGpt
= FALSE
;
1938 /* Dereference FileObject */
1939 ObDereferenceObject(FileObject
);
1942 /* If it's matching system string */
1943 if (RtlEqualString(&ArcNameStringA
, &ArcSystemString
, TRUE
))
1945 /* Then, fill in information about the system device */
1946 BootDiskInformation
->SystemDeviceSignature
= Signature
;
1948 /* Get its device object */
1949 Status
= IoGetDeviceObjectPointer(&DeviceStringW
,
1950 FILE_READ_ATTRIBUTES
,
1953 if (!NT_SUCCESS(Status
))
1955 RtlFreeUnicodeString(&DeviceStringW
);
1959 /* And call the drive to get information about partition */
1960 Irp
= IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX
,
1964 &PartitionInformation
,
1965 sizeof(PARTITION_INFORMATION_EX
),
1971 ObDereferenceObject(FileObject
);
1972 RtlFreeUnicodeString(&DeviceStringW
);
1976 /* Call & wait if needed */
1977 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
1978 Status
= IoCallDriver(DeviceObject
, Irp
);
1979 if (Status
== STATUS_PENDING
)
1981 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
1982 Status
= IoStatusBlock
.Status
;
1984 if (!NT_SUCCESS(Status
))
1986 ObDereferenceObject(FileObject
);
1987 RtlFreeUnicodeString(&DeviceStringW
);
1991 /* We get partition offset as demanded and return it */
1992 BootDiskInformation
->SystemPartitionOffset
= PartitionInformation
.StartingOffset
.QuadPart
;
1994 /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */
1995 if (IsBootDiskInfoEx
)
1997 /* Is PT MBR or GPT? */
1998 if (DriveLayout
->PartitionStyle
== PARTITION_STYLE_GPT
)
2000 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceGuid
= DriveLayout
->Gpt
.DiskId
;
2001 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= TRUE
;
2005 ((PBOOTDISK_INFORMATION_EX
)BootDiskInformation
)->SystemDeviceIsGpt
= FALSE
;
2009 /* Dereference FileObject */
2010 ObDereferenceObject(FileObject
);
2013 /* Release device string */
2014 RtlFreeUnicodeString(&DeviceStringW
);
2019 /* Finally, release drive layout structure */
2020 ExFreePool(DriveLayout
);
2032 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject
,
2033 IN ULONG BytesPerSector
,
2034 OUT PDISK_SIGNATURE Signature
)
2038 ULONG HeaderCRC32
, i
, CheckSum
;
2039 PEFI_PARTITION_HEADER EFIHeader
;
2040 PPARTITION_DESCRIPTOR PartitionDescriptor
;
2043 /* Ensure we'll read at least 512 bytes */
2044 if (BytesPerSector
< 512)
2046 BytesPerSector
= 512;
2049 /* Allocate a buffer for reading operations */
2050 Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
, BytesPerSector
, TAG_FSTUB
);
2053 return STATUS_NO_MEMORY
;
2056 /* Read first sector (sector 0) for MBR */
2057 Status
= FstubReadSector(DeviceObject
,
2061 if (!NT_SUCCESS(Status
))
2066 /* Get the partition descriptor array */
2067 PartitionDescriptor
= (PPARTITION_DESCRIPTOR
)
2068 &(Buffer
[PARTITION_TABLE_OFFSET
]);
2069 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
2070 if (PartitionDescriptor
[0].PartitionType
== EFI_PMBR_OSTYPE_EFI
&&
2071 PartitionDescriptor
[1].PartitionType
== 0 &&
2072 PartitionDescriptor
[2].PartitionType
== 0 &&
2073 PartitionDescriptor
[3].PartitionType
== 0)
2075 /* If we have GPT, read second sector (sector 1) for GPT header */
2076 Status
= FstubReadSector(DeviceObject
,
2080 if (!NT_SUCCESS(Status
))
2084 EFIHeader
= (PEFI_PARTITION_HEADER
)Buffer
;
2086 /* First check signature
2087 * Then, check version (we only support v1
2088 * Finally check header size
2090 if (EFIHeader
->Signature
!= EFI_HEADER_SIGNATURE
||
2091 EFIHeader
->Revision
!= EFI_HEADER_REVISION_1
||
2092 EFIHeader
->HeaderSize
!= sizeof(EFI_PARTITION_HEADER
))
2094 Status
= STATUS_DISK_CORRUPT_ERROR
;
2098 /* Save current checksum */
2099 HeaderCRC32
= EFIHeader
->HeaderCRC32
;
2100 /* Then zero the one in EFI header. This is needed to compute header checksum */
2101 EFIHeader
->HeaderCRC32
= 0;
2102 /* Compute header checksum and compare with the one present in partition table */
2103 if (RtlComputeCrc32(0, (PUCHAR
)Buffer
, sizeof(EFI_PARTITION_HEADER
)) != HeaderCRC32
)
2105 Status
= STATUS_DISK_CORRUPT_ERROR
;
2109 /* Set partition table style to GPT and return disk GUID */
2110 Signature
->PartitionStyle
= PARTITION_STYLE_GPT
;
2111 Signature
->Gpt
.DiskId
= EFIHeader
->DiskGUID
;
2115 /* Compute MBR checksum */
2116 for (i
= 0, CheckSum
= 0; i
< 512 / sizeof(ULONG
) ; i
++)
2118 CheckSum
+= Buffer
[i
];
2121 /* Set partition table style to MBR and return signature (offset 440) and checksum */
2122 Signature
->PartitionStyle
= PARTITION_STYLE_MBR
;
2123 Signature
->Mbr
.Signature
= Buffer
[PARTITION_TABLE_OFFSET
/ 2 - 1];
2124 Signature
->Mbr
.CheckSum
= CheckSum
;
2128 /* Free buffer and return */
2129 ExFreePoolWithTag(Buffer
, TAG_FSTUB
);
2138 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2139 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
** DriveLayout
)
2142 PDISK_INFORMATION Disk
;
2143 PARTITION_STYLE PartitionStyle
;
2146 ASSERT(DeviceObject
);
2147 ASSERT(DriveLayout
);
2149 /* First of all, allocate internal structure */
2150 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2151 if (!NT_SUCCESS(Status
))
2157 /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
2158 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2159 if (!NT_SUCCESS(Status
))
2161 FstubFreeDiskInformation(Disk
);
2165 /* Here partition table is really read, depending on its style */
2166 switch (PartitionStyle
)
2168 case PARTITION_STYLE_MBR
:
2169 case PARTITION_STYLE_RAW
:
2170 Status
= FstubReadPartitionTableMBR(Disk
, FALSE
, DriveLayout
);
2173 case PARTITION_STYLE_GPT
:
2174 /* Read primary table */
2175 Status
= FstubReadPartitionTableEFI(Disk
, FALSE
, DriveLayout
);
2176 /* If it failed, try reading backup table */
2177 if (!NT_SUCCESS(Status
))
2179 Status
= FstubReadPartitionTableEFI(Disk
, TRUE
, DriveLayout
);
2184 DPRINT("Unknown partition type\n");
2185 Status
= STATUS_UNSUCCESSFUL
;
2188 /* It's over, internal structure not needed anymore */
2189 FstubFreeDiskInformation(Disk
);
2191 /* In case of success, print data */
2192 if (NT_SUCCESS(Status
))
2194 FstubDbgPrintDriveLayoutEx(*DriveLayout
);
2205 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject
,
2206 IN ULONG PartitionNumber
,
2207 IN
struct _SET_PARTITION_INFORMATION_EX
* PartitionInfo
)
2210 PDISK_INFORMATION Disk
;
2211 PARTITION_STYLE PartitionStyle
;
2214 ASSERT(DeviceObject
);
2215 ASSERT(PartitionInfo
);
2217 /* Debug given modifications */
2218 FstubDbgPrintSetPartitionEx(PartitionInfo
, PartitionNumber
);
2220 /* Allocate internal structure */
2221 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2222 if (!NT_SUCCESS(Status
))
2227 /* Get partition table style on disk */
2228 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2229 if (!NT_SUCCESS(Status
))
2231 FstubFreeDiskInformation(Disk
);
2235 /* If it's not matching partition style given in modifications, give up */
2236 if (PartitionInfo
->PartitionStyle
!= PartitionStyle
)
2238 FstubFreeDiskInformation(Disk
);
2239 return STATUS_INVALID_PARAMETER
;
2242 /* Finally, handle modifications using proper function */
2243 switch (PartitionStyle
)
2245 case PARTITION_STYLE_MBR
:
2246 Status
= IoSetPartitionInformation(DeviceObject
,
2249 PartitionInfo
->Mbr
.PartitionType
);
2251 case PARTITION_STYLE_GPT
:
2252 Status
= FstubSetPartitionInformationEFI(Disk
,
2254 &(PartitionInfo
->Gpt
));
2257 Status
= STATUS_NOT_SUPPORTED
;
2260 /* Release internal structure and return */
2261 FstubFreeDiskInformation(Disk
);
2270 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject
,
2271 IN BOOLEAN FixErrors
)
2274 PDISK_INFORMATION Disk
;
2275 PARTITION_STYLE PartitionStyle
;
2278 ASSERT(DeviceObject
);
2280 /* Allocate internal structure */
2281 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, NULL
);
2282 if (!NT_SUCCESS(Status
))
2288 /* Get partition table style on disk */
2289 Status
= FstubDetectPartitionStyle(Disk
, &PartitionStyle
);
2290 if (!NT_SUCCESS(Status
))
2292 FstubFreeDiskInformation(Disk
);
2296 /* Action will depend on partition style */
2297 switch (PartitionStyle
)
2299 /* For MBR, assume it's always OK */
2300 case PARTITION_STYLE_MBR
:
2301 Status
= STATUS_SUCCESS
;
2303 /* For GPT, call internal function */
2304 case PARTITION_STYLE_GPT
:
2305 Status
= FstubVerifyPartitionTableEFI(Disk
, FixErrors
);
2307 /* Otherwise, signal we can't work */
2309 Status
= STATUS_NOT_SUPPORTED
;
2312 /* Release internal structure and return */
2313 FstubFreeDiskInformation(Disk
);
2322 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject
,
2323 IN
struct _DRIVE_LAYOUT_INFORMATION_EX
* DriveLayout
)
2326 PDISK_INFORMATION Disk
;
2327 ULONGLONG SectorsForPartitions
;
2328 EFI_PARTITION_HEADER EfiHeader
;
2331 ASSERT(DeviceObject
);
2332 ASSERT(DriveLayout
);
2334 /* Debug partition table that must be written */
2335 FstubDbgPrintDriveLayoutEx(DriveLayout
);
2337 /* Allocate internal structure */
2338 Status
= FstubAllocateDiskInformation(DeviceObject
, &Disk
, 0);
2339 if (!NT_SUCCESS(Status
))
2345 switch (DriveLayout
->PartitionStyle
)
2347 case PARTITION_STYLE_MBR
:
2348 Status
= FstubWritePartitionTableMBR(Disk
, DriveLayout
);
2351 case PARTITION_STYLE_GPT
:
2352 /* Read primary table header */
2353 Status
= FstubReadHeaderEFI(Disk
,
2356 /* If it failed, try reading back table header */
2357 if (!NT_SUCCESS(Status
))
2359 Status
= FstubReadHeaderEFI(Disk
,
2364 /* We have a header! */
2365 if (NT_SUCCESS(Status
))
2367 /* Check if there are enough places for the partitions to be written */
2368 if (DriveLayout
->PartitionCount
<= EfiHeader
.NumberOfEntries
)
2370 /* Count number of sectors needed to store partitions */
2371 SectorsForPartitions
= (EfiHeader
.NumberOfEntries
* PARTITION_ENTRY_SIZE
) / Disk
->SectorSize
;
2372 /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
2373 EfiHeader
.FirstUsableLBA
= SectorsForPartitions
+ 2;
2374 /* Set last usable LBA: Last sector - GPT header - Partitions entries */
2375 EfiHeader
.LastUsableLBA
= Disk
->SectorCount
- SectorsForPartitions
- 1;
2376 /* Write primary table */
2377 Status
= FstubWritePartitionTableEFI(Disk
,
2379 EfiHeader
.NumberOfEntries
,
2380 EfiHeader
.FirstUsableLBA
,
2381 EfiHeader
.LastUsableLBA
,
2383 DriveLayout
->PartitionCount
,
2384 DriveLayout
->PartitionEntry
);
2385 /* If it succeed, also update backup table */
2386 if (NT_SUCCESS(Status
))
2388 Status
= FstubWritePartitionTableEFI(Disk
,
2390 EfiHeader
.NumberOfEntries
,
2391 EfiHeader
.FirstUsableLBA
,
2392 EfiHeader
.LastUsableLBA
,
2394 DriveLayout
->PartitionCount
,
2395 DriveLayout
->PartitionEntry
);
2402 DPRINT("Unsupported partition style: %ld\n", DriveLayout
->PartitionStyle
);
2403 Status
= STATUS_NOT_SUPPORTED
;
2406 /* It's over, internal structure not needed anymore */
2407 FstubFreeDiskInformation(Disk
);