2 Routines supporting partition discovery and
5 Copyright (c) 2019 Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <IndustryStandard/Mbr.h>
12 #include <Uefi/UefiGpt.h>
13 #include <Library/BaseLib.h>
14 #include "FatLitePeim.h"
17 // Assumption: 'a' and 'blocksize' are all UINT32 or UINT64.
18 // If 'a' and 'blocksize' are not the same type, should use DivU64xU32 to calculate.
20 #define EFI_SIZE_TO_BLOCKS(a, blocksize) (((a) / (blocksize)) + (((a) % (blocksize)) ? 1 : 0))
23 // GPT Partition Entry Status
29 } EFI_PARTITION_ENTRY_STATUS
;
32 Check if the CRC field in the Partition table header is valid.
34 @param[in] PartHeader Partition table header structure
36 @retval TRUE the CRC is valid
37 @retval FALSE the CRC is invalid
41 PartitionCheckGptHeaderCRC (
42 IN EFI_PARTITION_TABLE_HEADER
*PartHeader
48 GptHdrCrc
= PartHeader
->Header
.CRC32
;
51 // Set CRC field to zero when doing calculation
53 PartHeader
->Header
.CRC32
= 0;
55 Crc
= CalculateCrc32 (PartHeader
, PartHeader
->Header
.HeaderSize
);
60 PartHeader
->Header
.CRC32
= GptHdrCrc
;
62 return (GptHdrCrc
== Crc
);
66 Check if the CRC field in the Partition table header is valid
67 for Partition entry array.
69 @param[in] PartHeader Partition table header structure
70 @param[in] PartEntry The partition entry array
72 @retval TRUE the CRC is valid
73 @retval FALSE the CRC is invalid
77 PartitionCheckGptEntryArrayCRC (
78 IN EFI_PARTITION_TABLE_HEADER
*PartHeader
,
79 IN EFI_PARTITION_ENTRY
*PartEntry
85 Size
= (UINTN
)MultU64x32 (PartHeader
->NumberOfPartitionEntries
, PartHeader
->SizeOfPartitionEntry
);
86 Crc
= CalculateCrc32 (PartEntry
, Size
);
88 return (BOOLEAN
)(PartHeader
->PartitionEntryArrayCRC32
== Crc
);
92 The function is used for valid GPT table. Both for Primary and Backup GPT header.
94 @param[in] PrivateData The global memory map
95 @param[in] ParentBlockDevNo The parent block device
96 @param[in] IsPrimaryHeader Indicate to which header will be checked.
97 @param[in] PartHdr Stores the partition table that is read
99 @retval TRUE The partition table is valid
100 @retval FALSE The partition table is not valid
104 PartitionCheckGptHeader (
105 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
106 IN UINTN ParentBlockDevNo
,
107 IN BOOLEAN IsPrimaryHeader
,
108 IN EFI_PARTITION_TABLE_HEADER
*PartHdr
111 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
113 EFI_PEI_LBA AlternateLba
;
114 EFI_PEI_LBA EntryArrayLastLba
;
116 UINT64 PartitionEntryArraySize
;
117 UINT64 PartitionEntryBlockNumb
;
118 UINT32 EntryArraySizeRemainder
;
120 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
122 if (IsPrimaryHeader
) {
123 Lba
= PRIMARY_PART_HEADER_LBA
;
124 AlternateLba
= ParentBlockDev
->LastBlock
;
126 Lba
= ParentBlockDev
->LastBlock
;
127 AlternateLba
= PRIMARY_PART_HEADER_LBA
;
130 if ((PartHdr
->Header
.Signature
!= EFI_PTAB_HEADER_ID
) ||
131 (PartHdr
->Header
.Revision
!= 0x00010000) ||
132 (PartHdr
->Header
.HeaderSize
< 92) ||
133 (PartHdr
->Header
.HeaderSize
> ParentBlockDev
->BlockSize
) ||
134 (!PartitionCheckGptHeaderCRC (PartHdr
)) ||
135 (PartHdr
->Header
.Reserved
!= 0)
138 DEBUG ((DEBUG_ERROR
, "Invalid efi partition table header\n"));
143 // | Block0 | Block1 |Block2 ~ FirstUsableLBA - 1|FirstUsableLBA, ... ,LastUsableLBA|LastUsableLBA+1 ~ LastBlock-1| LastBlock |
144 // |Protective MBR|Primary Header|Entry Array(At Least 16384)| Partition | Entry Array(At Least 16384) |BackUp Header|
146 // 1. Protective MBR is fixed at Block 0.
147 // 2. Primary Header is fixed at Block 1.
148 // 3. Backup Header is fixed at LastBlock.
149 // 4. Must be remain 128*128 bytes for primary entry array.
150 // 5. Must be remain 128*128 bytes for backup entry array.
151 // 6. SizeOfPartitionEntry must be equals to 128 * 2^n.
153 if ((PartHdr
->MyLBA
!= Lba
) ||
154 (PartHdr
->AlternateLBA
!= AlternateLba
) ||
155 (PartHdr
->FirstUsableLBA
< 2 + EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE
, ParentBlockDev
->BlockSize
)) ||
156 (PartHdr
->LastUsableLBA
> ParentBlockDev
->LastBlock
- 1 - EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE
, ParentBlockDev
->BlockSize
)) ||
157 (PartHdr
->FirstUsableLBA
> PartHdr
->LastUsableLBA
) ||
158 (PartHdr
->PartitionEntryLBA
< 2) ||
159 (PartHdr
->PartitionEntryLBA
> ParentBlockDev
->LastBlock
- 1) ||
160 ((PartHdr
->PartitionEntryLBA
>= PartHdr
->FirstUsableLBA
) && (PartHdr
->PartitionEntryLBA
<= PartHdr
->LastUsableLBA
)) ||
161 (PartHdr
->SizeOfPartitionEntry
%128 != 0) ||
162 (PartHdr
->SizeOfPartitionEntry
!= sizeof (EFI_PARTITION_ENTRY
))
165 DEBUG ((DEBUG_ERROR
, "Invalid efi partition table header\n"));
170 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
172 if (PartHdr
->NumberOfPartitionEntries
> DivU64x32 (MAX_UINTN
, PartHdr
->SizeOfPartitionEntry
)) {
173 DEBUG ((DEBUG_ERROR
, "Memory overflow in GPT Entry Array\n"));
177 PartitionEntryArraySize
= MultU64x32 (PartHdr
->NumberOfPartitionEntries
, PartHdr
->SizeOfPartitionEntry
);
178 EntryArraySizeRemainder
= 0;
179 PartitionEntryBlockNumb
= DivU64x32Remainder (PartitionEntryArraySize
, ParentBlockDev
->BlockSize
, &EntryArraySizeRemainder
);
180 if (EntryArraySizeRemainder
!= 0) {
181 PartitionEntryBlockNumb
++;
184 if (IsPrimaryHeader
) {
185 EntryArrayLastLba
= PartHdr
->FirstUsableLBA
;
187 EntryArrayLastLba
= ParentBlockDev
->LastBlock
;
191 // Make sure partition entry array not overlaps with partition area or the LastBlock.
193 if (PartHdr
->PartitionEntryLBA
+ PartitionEntryBlockNumb
> EntryArrayLastLba
) {
194 DEBUG ((DEBUG_ERROR
, "GPT Partition Entry Array Error!\n"));
195 DEBUG ((DEBUG_ERROR
, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize
));
196 DEBUG ((DEBUG_ERROR
, "PartitionEntryLBA = %lu.\n", PartHdr
->PartitionEntryLBA
));
197 DEBUG ((DEBUG_ERROR
, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb
));
198 DEBUG ((DEBUG_ERROR
, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba
));
206 This function is used to verify each partition in block device.
208 @param[in] PrivateData The global memory map
209 @param[in] ParentBlockDevNo The parent block device
210 @param[in] PartHdr Stores the partition table that is read
212 @retval TRUE The partition is valid
213 @retval FALSE The partition is not valid
217 PartitionCheckGptEntryArray (
218 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
219 IN UINTN ParentBlockDevNo
,
220 IN EFI_PARTITION_TABLE_HEADER
*PartHdr
224 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
225 PEI_FAT_BLOCK_DEVICE
*BlockDevPtr
;
227 UINT64 PartitionEntryArraySize
;
228 UINT64 PartitionEntryBlockNumb
;
229 UINT32 EntryArraySizeRemainder
;
231 EFI_PARTITION_ENTRY
*PartitionEntryBuffer
;
232 EFI_PARTITION_ENTRY_STATUS
*PartitionEntryStatus
;
240 EFI_PARTITION_ENTRY
*Entry
;
242 PartitionEntryBuffer
= NULL
;
243 PartitionEntryStatus
= NULL
;
245 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
248 PartitionEntryArraySize
= MultU64x32 (PartHdr
->NumberOfPartitionEntries
, PartHdr
->SizeOfPartitionEntry
);
249 EntryArraySizeRemainder
= 0;
250 PartitionEntryBlockNumb
= DivU64x32Remainder (PartitionEntryArraySize
, ParentBlockDev
->BlockSize
, &EntryArraySizeRemainder
);
251 if (EntryArraySizeRemainder
!= 0) {
252 PartitionEntryBlockNumb
++;
255 PartitionEntryArraySize
= MultU64x32 (PartitionEntryBlockNumb
, ParentBlockDev
->BlockSize
);
257 PartitionEntryBuffer
= (EFI_PARTITION_ENTRY
*)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN
)PartitionEntryArraySize
));
258 if (PartitionEntryBuffer
== NULL
) {
259 DEBUG ((DEBUG_ERROR
, "Allocate memory error!\n"));
263 PartitionEntryStatus
= (EFI_PARTITION_ENTRY_STATUS
*)AllocatePages (EFI_SIZE_TO_PAGES (PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
)));
264 if (PartitionEntryStatus
== NULL
) {
265 DEBUG ((DEBUG_ERROR
, "Allocate memory error!\n"));
269 ZeroMem (PartitionEntryStatus
, PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
));
271 Status
= FatReadBlock (
274 PartHdr
->PartitionEntryLBA
,
275 (UINTN
)PartitionEntryArraySize
,
278 if (EFI_ERROR (Status
)) {
279 DEBUG ((DEBUG_ERROR
, "Read partition entry array error!\n"));
283 if (!PartitionCheckGptEntryArrayCRC (PartHdr
, PartitionEntryBuffer
)) {
284 DEBUG ((DEBUG_ERROR
, "Partition entries CRC check fail\n"));
288 for (Index1
= 0; Index1
< PartHdr
->NumberOfPartitionEntries
; Index1
++) {
289 Entry
= (EFI_PARTITION_ENTRY
*)((UINT8
*)PartitionEntryBuffer
+ Index1
* PartHdr
->SizeOfPartitionEntry
);
290 if (CompareGuid (&Entry
->PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
)) {
294 StartingLBA
= Entry
->StartingLBA
;
295 EndingLBA
= Entry
->EndingLBA
;
296 if ((StartingLBA
> EndingLBA
) ||
297 (StartingLBA
< PartHdr
->FirstUsableLBA
) ||
298 (StartingLBA
> PartHdr
->LastUsableLBA
) ||
299 (EndingLBA
< PartHdr
->FirstUsableLBA
) ||
300 (EndingLBA
> PartHdr
->LastUsableLBA
)
303 PartitionEntryStatus
[Index1
].OutOfRange
= TRUE
;
307 if ((Entry
->Attributes
& BIT1
) != 0) {
309 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
311 PartitionEntryStatus
[Index1
].OsSpecific
= TRUE
;
314 for (Index2
= Index1
+ 1; Index2
< PartHdr
->NumberOfPartitionEntries
; Index2
++) {
315 Entry
= (EFI_PARTITION_ENTRY
*)((UINT8
*)PartitionEntryBuffer
+ Index2
* PartHdr
->SizeOfPartitionEntry
);
316 if (CompareGuid (&Entry
->PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
)) {
320 if ((Entry
->EndingLBA
>= StartingLBA
) && (Entry
->StartingLBA
<= EndingLBA
)) {
322 // This region overlaps with the Index1'th region
324 PartitionEntryStatus
[Index1
].Overlap
= TRUE
;
325 PartitionEntryStatus
[Index2
].Overlap
= TRUE
;
331 for (Index
= 0; Index
< PartHdr
->NumberOfPartitionEntries
; Index
++) {
332 if (CompareGuid (&PartitionEntryBuffer
[Index
].PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
) ||
333 PartitionEntryStatus
[Index
].OutOfRange
||
334 PartitionEntryStatus
[Index
].Overlap
||
335 PartitionEntryStatus
[Index
].OsSpecific
)
338 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
344 if (PrivateData
->BlockDeviceCount
>= PEI_FAT_MAX_BLOCK_DEVICE
) {
349 BlockDevPtr
= &(PrivateData
->BlockDevice
[PrivateData
->BlockDeviceCount
]);
351 BlockDevPtr
->BlockSize
= ParentBlockDev
->BlockSize
;
352 BlockDevPtr
->LastBlock
= PartitionEntryBuffer
[Index
].EndingLBA
;
353 BlockDevPtr
->IoAlign
= ParentBlockDev
->IoAlign
;
354 BlockDevPtr
->Logical
= TRUE
;
355 BlockDevPtr
->PartitionChecked
= FALSE
;
356 BlockDevPtr
->StartingPos
= MultU64x32 (
357 PartitionEntryBuffer
[Index
].StartingLBA
,
358 ParentBlockDev
->BlockSize
360 BlockDevPtr
->ParentDevNo
= ParentBlockDevNo
;
362 PrivateData
->BlockDeviceCount
++;
364 DEBUG ((DEBUG_INFO
, "Find GPT Partition [0x%lx", PartitionEntryBuffer
[Index
].StartingLBA
));
365 DEBUG ((DEBUG_INFO
, ", 0x%lx]\n", BlockDevPtr
->LastBlock
));
366 DEBUG ((DEBUG_INFO
, " BlockSize %x\n", BlockDevPtr
->BlockSize
));
370 if (PartitionEntryBuffer
!= NULL
) {
371 FreePages (PartitionEntryBuffer
, EFI_SIZE_TO_PAGES ((UINTN
)PartitionEntryArraySize
));
374 if (PartitionEntryStatus
!= NULL
) {
375 FreePages (PartitionEntryStatus
, EFI_SIZE_TO_PAGES (PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
)));
382 The function is used to check GPT structure, include GPT header and GPT entry array.
385 2. Check partition entry array.
386 3. Check each partitions.
388 @param[in] PrivateData The global memory map
389 @param[in] ParentBlockDevNo The parent block device
390 @param[in] IsPrimary Indicate primary or backup to be check
392 @retval TRUE Primary or backup GPT structure is valid.
393 @retval FALSE Both primary and backup are invalid.
397 PartitionCheckGptStructure (
398 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
399 IN UINTN ParentBlockDevNo
,
404 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
405 EFI_PARTITION_TABLE_HEADER
*PartHdr
;
406 EFI_PEI_LBA GptHeaderLBA
;
408 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
409 PartHdr
= (EFI_PARTITION_TABLE_HEADER
*)PrivateData
->BlockData
;
412 GptHeaderLBA
= PRIMARY_PART_HEADER_LBA
;
414 GptHeaderLBA
= ParentBlockDev
->LastBlock
;
417 Status
= FatReadBlock (
421 ParentBlockDev
->BlockSize
,
424 if (EFI_ERROR (Status
)) {
428 if (!PartitionCheckGptHeader (PrivateData
, ParentBlockDevNo
, IsPrimary
, PartHdr
)) {
432 if (!PartitionCheckGptEntryArray (PrivateData
, ParentBlockDevNo
, PartHdr
)) {
440 This function is used to check protective MBR structure before checking GPT.
442 @param[in] PrivateData The global memory map
443 @param[in] ParentBlockDevNo The parent block device
445 @retval TRUE Valid protective MBR
446 @retval FALSE Invalid MBR
449 PartitionCheckProtectiveMbr (
450 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
451 IN UINTN ParentBlockDevNo
455 MASTER_BOOT_RECORD
*ProtectiveMbr
;
456 MBR_PARTITION_RECORD
*MbrPartition
;
457 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
460 ProtectiveMbr
= (MASTER_BOOT_RECORD
*)PrivateData
->BlockData
;
461 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
464 // Read Protective MBR
466 Status
= FatReadBlock (
470 ParentBlockDev
->BlockSize
,
473 if (EFI_ERROR (Status
)) {
474 DEBUG ((DEBUG_ERROR
, "GPT Error When Read Protective Mbr From Partition!\n"));
478 if (ProtectiveMbr
->Signature
!= MBR_SIGNATURE
) {
479 DEBUG ((DEBUG_ERROR
, "Protective Mbr Signature is invalid!\n"));
484 // The partition define in UEFI Spec Table 17.
485 // Boot Code, Unique MBR Disk Signature, Unknown.
486 // These parts will not be used by UEFI, so we skip to check them.
488 for (Index
= 0; Index
< MAX_MBR_PARTITIONS
; Index
++) {
489 MbrPartition
= (MBR_PARTITION_RECORD
*)&ProtectiveMbr
->Partition
[Index
];
490 if ((MbrPartition
->BootIndicator
== 0x00) &&
491 (MbrPartition
->StartSector
== 0x02) &&
492 (MbrPartition
->OSIndicator
== PMBR_GPT_PARTITION
) &&
493 (UNPACK_UINT32 (MbrPartition
->StartingLBA
) == 1)
500 DEBUG ((DEBUG_ERROR
, "Protective Mbr, All Partition Entry Are Empty!\n"));
505 This function is used for finding GPT partition on block device.
506 As follow UEFI spec we should check protective MBR first and then
507 try to check both primary/backup GPT structures.
509 @param[in] PrivateData The global memory map
510 @param[in] ParentBlockDevNo The parent block device
512 @retval TRUE New partitions are detected and logical block devices
513 are added to block device array
514 @retval FALSE No new partitions are added
518 FatFindGptPartitions (
519 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
520 IN UINTN ParentBlockDevNo
524 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
526 if (ParentBlockDevNo
> PEI_FAT_MAX_BLOCK_DEVICE
- 1) {
530 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
531 if (ParentBlockDev
->BlockSize
> PEI_FAT_MAX_BLOCK_SIZE
) {
532 DEBUG ((DEBUG_ERROR
, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev
->BlockSize
));
536 if (!PartitionCheckProtectiveMbr (PrivateData
, ParentBlockDevNo
)) {
540 Found
= PartitionCheckGptStructure (PrivateData
, ParentBlockDevNo
, TRUE
);
542 DEBUG ((DEBUG_ERROR
, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
543 Found
= PartitionCheckGptStructure (PrivateData
, ParentBlockDevNo
, FALSE
);
547 ParentBlockDev
->PartitionChecked
= TRUE
;