]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/FatPei/Gpt.c
Maintainers.txt: update mailing list information
[mirror_edk2.git] / FatPkg / FatPei / Gpt.c
1 /** @file
2 Routines supporting partition discovery and
3 logical device reading
4
5 Copyright (c) 2019 Intel Corporation. All rights reserved.<BR>
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <IndustryStandard/Mbr.h>
12 #include <Uefi/UefiGpt.h>
13 #include <Library/BaseLib.h>
14 #include "FatLitePeim.h"
15
16 //
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.
19 //
20 #define EFI_SIZE_TO_BLOCKS(a, blocksize) (((a) / (blocksize)) + (((a) % (blocksize)) ? 1 : 0))
21
22 //
23 // GPT Partition Entry Status
24 //
25 typedef struct {
26 BOOLEAN OutOfRange;
27 BOOLEAN Overlap;
28 BOOLEAN OsSpecific;
29 } EFI_PARTITION_ENTRY_STATUS;
30
31 /**
32 Check if the CRC field in the Partition table header is valid.
33
34 @param[in] PartHeader Partition table header structure
35
36 @retval TRUE the CRC is valid
37 @retval FALSE the CRC is invalid
38
39 **/
40 BOOLEAN
41 PartitionCheckGptHeaderCRC (
42 IN EFI_PARTITION_TABLE_HEADER *PartHeader
43 )
44 {
45 UINT32 GptHdrCrc;
46 UINT32 Crc;
47
48 GptHdrCrc = PartHeader->Header.CRC32;
49
50 //
51 // Set CRC field to zero when doing calcuation
52 //
53 PartHeader->Header.CRC32 = 0;
54
55 Crc = CalculateCrc32 (PartHeader, PartHeader->Header.HeaderSize);
56
57 //
58 // Restore Header CRC
59 //
60 PartHeader->Header.CRC32 = GptHdrCrc;
61
62 return (GptHdrCrc == Crc);
63 }
64
65
66 /**
67 Check if the CRC field in the Partition table header is valid
68 for Partition entry array.
69
70 @param[in] PartHeader Partition table header structure
71 @param[in] PartEntry The partition entry array
72
73 @retval TRUE the CRC is valid
74 @retval FALSE the CRC is invalid
75
76 **/
77 BOOLEAN
78 PartitionCheckGptEntryArrayCRC (
79 IN EFI_PARTITION_TABLE_HEADER *PartHeader,
80 IN EFI_PARTITION_ENTRY *PartEntry
81 )
82 {
83 UINT32 Crc;
84 UINTN Size;
85
86 Size = (UINTN)MultU64x32(PartHeader->NumberOfPartitionEntries, PartHeader->SizeOfPartitionEntry);
87 Crc = CalculateCrc32 (PartEntry, Size);
88
89 return (BOOLEAN) (PartHeader->PartitionEntryArrayCRC32 == Crc);
90 }
91
92 /**
93 The function is used for valid GPT table. Both for Primary and Backup GPT header.
94
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
99
100 @retval TRUE The partition table is valid
101 @retval FALSE The partition table is not valid
102
103 **/
104 BOOLEAN
105 PartitionCheckGptHeader (
106 IN PEI_FAT_PRIVATE_DATA *PrivateData,
107 IN UINTN ParentBlockDevNo,
108 IN BOOLEAN IsPrimaryHeader,
109 IN EFI_PARTITION_TABLE_HEADER *PartHdr
110 )
111 {
112 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
113 EFI_PEI_LBA Lba;
114 EFI_PEI_LBA AlternateLba;
115 EFI_PEI_LBA EntryArrayLastLba;
116
117 UINT64 PartitionEntryArraySize;
118 UINT64 PartitionEntryBlockNumb;
119 UINT32 EntryArraySizeRemainder;
120
121 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
122
123 if (IsPrimaryHeader) {
124 Lba = PRIMARY_PART_HEADER_LBA;
125 AlternateLba = ParentBlockDev->LastBlock;
126 } else {
127 Lba = ParentBlockDev->LastBlock;
128 AlternateLba = PRIMARY_PART_HEADER_LBA;
129 }
130
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)
137 ) {
138 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
139 return FALSE;
140 }
141
142 //
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|
145 //
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.
152 //
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))
163 ) {
164 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
165 return FALSE;
166 }
167
168 //
169 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
170 //
171 if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) {
172 DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n"));
173 return FALSE;
174 }
175
176 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
177 EntryArraySizeRemainder = 0;
178 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
179 if (EntryArraySizeRemainder != 0) {
180 PartitionEntryBlockNumb++;
181 }
182
183 if (IsPrimaryHeader) {
184 EntryArrayLastLba = PartHdr->FirstUsableLBA;
185 } else {
186 EntryArrayLastLba = ParentBlockDev->LastBlock;
187 }
188
189 //
190 // Make sure partition entry array not overlaps with partition area or the LastBlock.
191 //
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));
198 return FALSE;
199 }
200
201 return TRUE;
202 }
203
204 /**
205 This function is used to verify each partition in block device.
206
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
210
211 @retval TRUE The partition is valid
212 @retval FALSE The partition is not valid
213
214 **/
215 BOOLEAN
216 PartitionCheckGptEntryArray (
217 IN PEI_FAT_PRIVATE_DATA *PrivateData,
218 IN UINTN ParentBlockDevNo,
219 IN EFI_PARTITION_TABLE_HEADER *PartHdr
220 )
221 {
222 EFI_STATUS Status;
223 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
224 PEI_FAT_BLOCK_DEVICE *BlockDevPtr;
225
226 UINT64 PartitionEntryArraySize;
227 UINT64 PartitionEntryBlockNumb;
228 UINT32 EntryArraySizeRemainder;
229
230 EFI_PARTITION_ENTRY *PartitionEntryBuffer;
231 EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus;
232
233 BOOLEAN Found;
234 EFI_LBA StartingLBA;
235 EFI_LBA EndingLBA;
236 UINTN Index;
237 UINTN Index1;
238 UINTN Index2;
239 EFI_PARTITION_ENTRY *Entry;
240
241 PartitionEntryBuffer = NULL;
242 PartitionEntryStatus = NULL;
243
244 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
245 Found = FALSE;
246
247 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
248 EntryArraySizeRemainder = 0;
249 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
250 if (EntryArraySizeRemainder != 0) {
251 PartitionEntryBlockNumb++;
252 }
253 PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
254
255 PartitionEntryBuffer = (EFI_PARTITION_ENTRY *) AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
256 if (PartitionEntryBuffer == NULL) {
257 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
258 goto EXIT;
259 }
260
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"));
264 goto EXIT;
265 }
266 ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
267
268 Status = FatReadBlock (
269 PrivateData,
270 ParentBlockDevNo,
271 PartHdr->PartitionEntryLBA,
272 (UINTN)PartitionEntryArraySize,
273 PartitionEntryBuffer
274 );
275 if (EFI_ERROR (Status)) {
276 DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n"));
277 goto EXIT;
278 }
279
280 if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) {
281 DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n"));
282 goto EXIT;
283 }
284
285 for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) {
286 Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry);
287 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
288 continue;
289 }
290
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
298 ) {
299 PartitionEntryStatus[Index1].OutOfRange = TRUE;
300 continue;
301 }
302
303 if ((Entry->Attributes & BIT1) != 0) {
304 //
305 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
306 //
307 PartitionEntryStatus[Index1].OsSpecific = TRUE;
308 }
309
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)) {
313 continue;
314 }
315
316 if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) {
317 //
318 // This region overlaps with the Index1'th region
319 //
320 PartitionEntryStatus[Index1].Overlap = TRUE;
321 PartitionEntryStatus[Index2].Overlap = TRUE;
322 continue;
323 }
324 }
325 }
326
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) {
332 //
333 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
334 // partition Entries
335 //
336 continue;
337 }
338
339 if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
340 break;
341 }
342
343 Found = TRUE;
344 BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
345
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
354 );
355 BlockDevPtr->ParentDevNo = ParentBlockDevNo;
356
357 PrivateData->BlockDeviceCount++;
358
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));
362 }
363
364 EXIT:
365 if (PartitionEntryBuffer != NULL) {
366 FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
367 }
368
369 if (PartitionEntryStatus != NULL) {
370 FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
371 }
372
373 return Found;
374 }
375
376 /**
377 The function is used to check GPT structure, include GPT header and GPT entry array.
378
379 1. Check GPT header.
380 2. Check partition entry array.
381 3. Check each partitions.
382
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
386
387 @retval TRUE Primary or backup GPT structure is valid.
388 @retval FALSE Both primary and backup are invalid.
389
390 **/
391 BOOLEAN
392 PartitionCheckGptStructure (
393 IN PEI_FAT_PRIVATE_DATA *PrivateData,
394 IN UINTN ParentBlockDevNo,
395 IN BOOLEAN IsPrimary
396 )
397 {
398 EFI_STATUS Status;
399 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
400 EFI_PARTITION_TABLE_HEADER *PartHdr;
401 EFI_PEI_LBA GptHeaderLBA;
402
403 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
404 PartHdr = (EFI_PARTITION_TABLE_HEADER *) PrivateData->BlockData;
405
406 if (IsPrimary) {
407 GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
408 } else {
409 GptHeaderLBA = ParentBlockDev->LastBlock;
410 }
411
412 Status = FatReadBlock (
413 PrivateData,
414 ParentBlockDevNo,
415 GptHeaderLBA,
416 ParentBlockDev->BlockSize,
417 PartHdr
418 );
419 if (EFI_ERROR (Status)) {
420 return FALSE;
421 }
422
423 if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
424 return FALSE;
425 }
426
427 if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
428 return FALSE;
429 }
430
431 return TRUE;
432 }
433
434 /**
435 This function is used to check protective MBR structure before checking GPT.
436
437 @param[in] PrivateData The global memory map
438 @param[in] ParentBlockDevNo The parent block device
439
440 @retval TRUE Valid protective MBR
441 @retval FALSE Invalid MBR
442 **/
443 BOOLEAN
444 PartitionCheckProtectiveMbr (
445 IN PEI_FAT_PRIVATE_DATA *PrivateData,
446 IN UINTN ParentBlockDevNo
447 )
448 {
449 EFI_STATUS Status;
450 MASTER_BOOT_RECORD *ProtectiveMbr;
451 MBR_PARTITION_RECORD *MbrPartition;
452 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
453 UINTN Index;
454
455 ProtectiveMbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData;
456 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
457
458 //
459 // Read Protective MBR
460 //
461 Status = FatReadBlock (
462 PrivateData,
463 ParentBlockDevNo,
464 0,
465 ParentBlockDev->BlockSize,
466 ProtectiveMbr
467 );
468 if (EFI_ERROR (Status)) {
469 DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
470 return FALSE;
471 }
472
473 if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
474 DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
475 return FALSE;
476 }
477
478 //
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.
482 //
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
489 ) {
490 return TRUE;
491 }
492 }
493
494 DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n"));
495 return FALSE;
496 }
497
498 /**
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.
502
503 @param[in] PrivateData The global memory map
504 @param[in] ParentBlockDevNo The parent block device
505
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
509
510 **/
511 BOOLEAN
512 FatFindGptPartitions (
513 IN PEI_FAT_PRIVATE_DATA *PrivateData,
514 IN UINTN ParentBlockDevNo
515 )
516 {
517 BOOLEAN Found;
518 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
519
520 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
521 return FALSE;
522 }
523
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));
527 return FALSE;
528 }
529
530 if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) {
531 return FALSE;
532 }
533
534 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE);
535 if (!Found) {
536 DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
537 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE);
538 }
539
540 if (Found) {
541 ParentBlockDev->PartitionChecked = TRUE;
542 }
543
544 return Found;
545 }