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
);
67 Check if the CRC field in the Partition table header is valid
68 for Partition entry array.
70 @param[in] PartHeader Partition table header structure
71 @param[in] PartEntry The partition entry array
73 @retval TRUE the CRC is valid
74 @retval FALSE the CRC is invalid
78 PartitionCheckGptEntryArrayCRC (
79 IN EFI_PARTITION_TABLE_HEADER
*PartHeader
,
80 IN EFI_PARTITION_ENTRY
*PartEntry
86 Size
= (UINTN
)MultU64x32(PartHeader
->NumberOfPartitionEntries
, PartHeader
->SizeOfPartitionEntry
);
87 Crc
= CalculateCrc32 (PartEntry
, Size
);
89 return (BOOLEAN
) (PartHeader
->PartitionEntryArrayCRC32
== Crc
);
93 The function is used for valid GPT table. Both for Primary and Backup GPT header.
95 @param[in] PrivateData The global memory map
96 @param[in] ParentBlockDevNo The parent block device
97 @param[in] IsPrimaryHeader Indicate to which header will be checked.
98 @param[in] PartHdr Stores the partition table that is read
100 @retval TRUE The partition table is valid
101 @retval FALSE The partition table is not valid
105 PartitionCheckGptHeader (
106 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
107 IN UINTN ParentBlockDevNo
,
108 IN BOOLEAN IsPrimaryHeader
,
109 IN EFI_PARTITION_TABLE_HEADER
*PartHdr
112 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
114 EFI_PEI_LBA AlternateLba
;
115 EFI_PEI_LBA EntryArrayLastLba
;
117 UINT64 PartitionEntryArraySize
;
118 UINT64 PartitionEntryBlockNumb
;
119 UINT32 EntryArraySizeRemainder
;
121 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
123 if (IsPrimaryHeader
) {
124 Lba
= PRIMARY_PART_HEADER_LBA
;
125 AlternateLba
= ParentBlockDev
->LastBlock
;
127 Lba
= ParentBlockDev
->LastBlock
;
128 AlternateLba
= PRIMARY_PART_HEADER_LBA
;
131 if ( (PartHdr
->Header
.Signature
!= EFI_PTAB_HEADER_ID
) ||
132 (PartHdr
->Header
.Revision
!= 0x00010000) ||
133 (PartHdr
->Header
.HeaderSize
< 92) ||
134 (PartHdr
->Header
.HeaderSize
> ParentBlockDev
->BlockSize
) ||
135 (!PartitionCheckGptHeaderCRC (PartHdr
)) ||
136 (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
))
164 DEBUG ((DEBUG_ERROR
, "Invalid efi partition table header\n"));
169 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
171 if (PartHdr
->NumberOfPartitionEntries
> DivU64x32 (MAX_UINTN
, PartHdr
->SizeOfPartitionEntry
)) {
172 DEBUG ((DEBUG_ERROR
, "Memory overflow in GPT Entry Array\n"));
176 PartitionEntryArraySize
= MultU64x32 (PartHdr
->NumberOfPartitionEntries
, PartHdr
->SizeOfPartitionEntry
);
177 EntryArraySizeRemainder
= 0;
178 PartitionEntryBlockNumb
= DivU64x32Remainder (PartitionEntryArraySize
, ParentBlockDev
->BlockSize
, &EntryArraySizeRemainder
);
179 if (EntryArraySizeRemainder
!= 0) {
180 PartitionEntryBlockNumb
++;
183 if (IsPrimaryHeader
) {
184 EntryArrayLastLba
= PartHdr
->FirstUsableLBA
;
186 EntryArrayLastLba
= ParentBlockDev
->LastBlock
;
190 // Make sure partition entry array not overlaps with partition area or the LastBlock.
192 if (PartHdr
->PartitionEntryLBA
+ PartitionEntryBlockNumb
> EntryArrayLastLba
) {
193 DEBUG ((DEBUG_ERROR
, "GPT Partition Entry Array Error!\n"));
194 DEBUG ((DEBUG_ERROR
, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize
));
195 DEBUG ((DEBUG_ERROR
, "PartitionEntryLBA = %lu.\n", PartHdr
->PartitionEntryLBA
));
196 DEBUG ((DEBUG_ERROR
, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb
));
197 DEBUG ((DEBUG_ERROR
, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba
));
205 This function is used to verify each partition in block device.
207 @param[in] PrivateData The global memory map
208 @param[in] ParentBlockDevNo The parent block device
209 @param[in] PartHdr Stores the partition table that is read
211 @retval TRUE The partition is valid
212 @retval FALSE The partition is not valid
216 PartitionCheckGptEntryArray (
217 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
218 IN UINTN ParentBlockDevNo
,
219 IN EFI_PARTITION_TABLE_HEADER
*PartHdr
223 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
224 PEI_FAT_BLOCK_DEVICE
*BlockDevPtr
;
226 UINT64 PartitionEntryArraySize
;
227 UINT64 PartitionEntryBlockNumb
;
228 UINT32 EntryArraySizeRemainder
;
230 EFI_PARTITION_ENTRY
*PartitionEntryBuffer
;
231 EFI_PARTITION_ENTRY_STATUS
*PartitionEntryStatus
;
239 EFI_PARTITION_ENTRY
*Entry
;
241 PartitionEntryBuffer
= NULL
;
242 PartitionEntryStatus
= NULL
;
244 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
247 PartitionEntryArraySize
= MultU64x32 (PartHdr
->NumberOfPartitionEntries
, PartHdr
->SizeOfPartitionEntry
);
248 EntryArraySizeRemainder
= 0;
249 PartitionEntryBlockNumb
= DivU64x32Remainder (PartitionEntryArraySize
, ParentBlockDev
->BlockSize
, &EntryArraySizeRemainder
);
250 if (EntryArraySizeRemainder
!= 0) {
251 PartitionEntryBlockNumb
++;
253 PartitionEntryArraySize
= MultU64x32 (PartitionEntryBlockNumb
, ParentBlockDev
->BlockSize
);
255 PartitionEntryBuffer
= (EFI_PARTITION_ENTRY
*) AllocatePages (EFI_SIZE_TO_PAGES ((UINTN
)PartitionEntryArraySize
));
256 if (PartitionEntryBuffer
== NULL
) {
257 DEBUG ((DEBUG_ERROR
, "Allocate memory error!\n"));
261 PartitionEntryStatus
= (EFI_PARTITION_ENTRY_STATUS
*) AllocatePages (EFI_SIZE_TO_PAGES (PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
)));
262 if (PartitionEntryStatus
== NULL
) {
263 DEBUG ((DEBUG_ERROR
, "Allocate memory error!\n"));
266 ZeroMem (PartitionEntryStatus
, PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
));
268 Status
= FatReadBlock (
271 PartHdr
->PartitionEntryLBA
,
272 (UINTN
)PartitionEntryArraySize
,
275 if (EFI_ERROR (Status
)) {
276 DEBUG ((DEBUG_ERROR
, "Read partition entry array error!\n"));
280 if (!PartitionCheckGptEntryArrayCRC (PartHdr
, PartitionEntryBuffer
)) {
281 DEBUG ((DEBUG_ERROR
, "Partition entries CRC check fail\n"));
285 for (Index1
= 0; Index1
< PartHdr
->NumberOfPartitionEntries
; Index1
++) {
286 Entry
= (EFI_PARTITION_ENTRY
*) ((UINT8
*) PartitionEntryBuffer
+ Index1
* PartHdr
->SizeOfPartitionEntry
);
287 if (CompareGuid (&Entry
->PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
)) {
291 StartingLBA
= Entry
->StartingLBA
;
292 EndingLBA
= Entry
->EndingLBA
;
293 if (StartingLBA
> EndingLBA
||
294 StartingLBA
< PartHdr
->FirstUsableLBA
||
295 StartingLBA
> PartHdr
->LastUsableLBA
||
296 EndingLBA
< PartHdr
->FirstUsableLBA
||
297 EndingLBA
> PartHdr
->LastUsableLBA
299 PartitionEntryStatus
[Index1
].OutOfRange
= TRUE
;
303 if ((Entry
->Attributes
& BIT1
) != 0) {
305 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
307 PartitionEntryStatus
[Index1
].OsSpecific
= TRUE
;
310 for (Index2
= Index1
+ 1; Index2
< PartHdr
->NumberOfPartitionEntries
; Index2
++) {
311 Entry
= (EFI_PARTITION_ENTRY
*) ((UINT8
*) PartitionEntryBuffer
+ Index2
* PartHdr
->SizeOfPartitionEntry
);
312 if (CompareGuid (&Entry
->PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
)) {
316 if (Entry
->EndingLBA
>= StartingLBA
&& Entry
->StartingLBA
<= EndingLBA
) {
318 // This region overlaps with the Index1'th region
320 PartitionEntryStatus
[Index1
].Overlap
= TRUE
;
321 PartitionEntryStatus
[Index2
].Overlap
= TRUE
;
327 for (Index
= 0; Index
< PartHdr
->NumberOfPartitionEntries
; Index
++) {
328 if (CompareGuid (&PartitionEntryBuffer
[Index
].PartitionTypeGUID
, &gEfiPartTypeUnusedGuid
)||
329 PartitionEntryStatus
[Index
].OutOfRange
||
330 PartitionEntryStatus
[Index
].Overlap
||
331 PartitionEntryStatus
[Index
].OsSpecific
) {
333 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
339 if (PrivateData
->BlockDeviceCount
>= PEI_FAT_MAX_BLOCK_DEVICE
) {
344 BlockDevPtr
= &(PrivateData
->BlockDevice
[PrivateData
->BlockDeviceCount
]);
346 BlockDevPtr
->BlockSize
= ParentBlockDev
->BlockSize
;
347 BlockDevPtr
->LastBlock
= PartitionEntryBuffer
[Index
].EndingLBA
;
348 BlockDevPtr
->IoAlign
= ParentBlockDev
->IoAlign
;
349 BlockDevPtr
->Logical
= TRUE
;
350 BlockDevPtr
->PartitionChecked
= FALSE
;
351 BlockDevPtr
->StartingPos
= MultU64x32 (
352 PartitionEntryBuffer
[Index
].StartingLBA
,
353 ParentBlockDev
->BlockSize
355 BlockDevPtr
->ParentDevNo
= ParentBlockDevNo
;
357 PrivateData
->BlockDeviceCount
++;
359 DEBUG ((DEBUG_INFO
, "Find GPT Partition [0x%lx", PartitionEntryBuffer
[Index
].StartingLBA
, BlockDevPtr
->LastBlock
));
360 DEBUG ((DEBUG_INFO
, ", 0x%lx]\n", BlockDevPtr
->LastBlock
));
361 DEBUG ((DEBUG_INFO
, " BlockSize %x\n", BlockDevPtr
->BlockSize
));
365 if (PartitionEntryBuffer
!= NULL
) {
366 FreePages (PartitionEntryBuffer
, EFI_SIZE_TO_PAGES ((UINTN
)PartitionEntryArraySize
));
369 if (PartitionEntryStatus
!= NULL
) {
370 FreePages (PartitionEntryStatus
, EFI_SIZE_TO_PAGES (PartHdr
->NumberOfPartitionEntries
* sizeof (EFI_PARTITION_ENTRY_STATUS
)));
377 The function is used to check GPT structure, include GPT header and GPT entry array.
380 2. Check partition entry array.
381 3. Check each partitions.
383 @param[in] PrivateData The global memory map
384 @param[in] ParentBlockDevNo The parent block device
385 @param[in] IsPrimary Indicate primary or backup to be check
387 @retval TRUE Primary or backup GPT structure is valid.
388 @retval FALSE Both primary and backup are invalid.
392 PartitionCheckGptStructure (
393 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
394 IN UINTN ParentBlockDevNo
,
399 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
400 EFI_PARTITION_TABLE_HEADER
*PartHdr
;
401 EFI_PEI_LBA GptHeaderLBA
;
403 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
404 PartHdr
= (EFI_PARTITION_TABLE_HEADER
*) PrivateData
->BlockData
;
407 GptHeaderLBA
= PRIMARY_PART_HEADER_LBA
;
409 GptHeaderLBA
= ParentBlockDev
->LastBlock
;
412 Status
= FatReadBlock (
416 ParentBlockDev
->BlockSize
,
419 if (EFI_ERROR (Status
)) {
423 if (!PartitionCheckGptHeader (PrivateData
, ParentBlockDevNo
, IsPrimary
, PartHdr
)) {
427 if (!PartitionCheckGptEntryArray (PrivateData
, ParentBlockDevNo
, PartHdr
)) {
435 This function is used to check protective MBR structure before checking GPT.
437 @param[in] PrivateData The global memory map
438 @param[in] ParentBlockDevNo The parent block device
440 @retval TRUE Valid protective MBR
441 @retval FALSE Invalid MBR
444 PartitionCheckProtectiveMbr (
445 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
446 IN UINTN ParentBlockDevNo
450 MASTER_BOOT_RECORD
*ProtectiveMbr
;
451 MBR_PARTITION_RECORD
*MbrPartition
;
452 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
455 ProtectiveMbr
= (MASTER_BOOT_RECORD
*) PrivateData
->BlockData
;
456 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
459 // Read Protective MBR
461 Status
= FatReadBlock (
465 ParentBlockDev
->BlockSize
,
468 if (EFI_ERROR (Status
)) {
469 DEBUG ((DEBUG_ERROR
, "GPT Error When Read Protective Mbr From Partition!\n"));
473 if (ProtectiveMbr
->Signature
!= MBR_SIGNATURE
) {
474 DEBUG ((DEBUG_ERROR
, "Protective Mbr Signature is invalid!\n"));
479 // The partition define in UEFI Spec Table 17.
480 // Boot Code, Unique MBR Disk Signature, Unknown.
481 // These parts will not be used by UEFI, so we skip to check them.
483 for (Index
= 0; Index
< MAX_MBR_PARTITIONS
; Index
++) {
484 MbrPartition
= (MBR_PARTITION_RECORD
*)&ProtectiveMbr
->Partition
[Index
];
485 if (MbrPartition
->BootIndicator
== 0x00 &&
486 MbrPartition
->StartSector
== 0x02 &&
487 MbrPartition
->OSIndicator
== PMBR_GPT_PARTITION
&&
488 UNPACK_UINT32 (MbrPartition
->StartingLBA
) == 1
494 DEBUG ((DEBUG_ERROR
, "Protective Mbr, All Partition Entry Are Empty!\n"));
499 This function is used for finding GPT partition on block device.
500 As follow UEFI spec we should check protective MBR first and then
501 try to check both primary/backup GPT structures.
503 @param[in] PrivateData The global memory map
504 @param[in] ParentBlockDevNo The parent block device
506 @retval TRUE New partitions are detected and logical block devices
507 are added to block device array
508 @retval FALSE No new partitions are added
512 FatFindGptPartitions (
513 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
514 IN UINTN ParentBlockDevNo
518 PEI_FAT_BLOCK_DEVICE
*ParentBlockDev
;
520 if (ParentBlockDevNo
> PEI_FAT_MAX_BLOCK_DEVICE
- 1) {
524 ParentBlockDev
= &(PrivateData
->BlockDevice
[ParentBlockDevNo
]);
525 if (ParentBlockDev
->BlockSize
> PEI_FAT_MAX_BLOCK_SIZE
) {
526 DEBUG ((DEBUG_ERROR
, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev
->BlockSize
));
530 if (!PartitionCheckProtectiveMbr (PrivateData
, ParentBlockDevNo
)) {
534 Found
= PartitionCheckGptStructure (PrivateData
, ParentBlockDevNo
, TRUE
);
536 DEBUG ((DEBUG_ERROR
, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
537 Found
= PartitionCheckGptStructure (PrivateData
, ParentBlockDevNo
, FALSE
);
541 ParentBlockDev
->PartitionChecked
= TRUE
;