]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/FatPei/Gpt.c
MdeModulePkg/PiSmmCore: Control S3 related functionality through flag.
[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 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <IndustryStandard/Mbr.h>
18 #include <Uefi/UefiGpt.h>
19 #include <Library/BaseLib.h>
20 #include "FatLitePeim.h"
21
22 //
23 // Assumption: 'a' and 'blocksize' are all UINT32 or UINT64.
24 // If 'a' and 'blocksize' are not the same type, should use DivU64xU32 to calculate.
25 //
26 #define EFI_SIZE_TO_BLOCKS(a, blocksize) (((a) / (blocksize)) + (((a) % (blocksize)) ? 1 : 0))
27
28 //
29 // GPT Partition Entry Status
30 //
31 typedef struct {
32 BOOLEAN OutOfRange;
33 BOOLEAN Overlap;
34 BOOLEAN OsSpecific;
35 } EFI_PARTITION_ENTRY_STATUS;
36
37 /**
38 Check if the CRC field in the Partition table header is valid.
39
40 @param[in] PartHeader Partition table header structure
41
42 @retval TRUE the CRC is valid
43 @retval FALSE the CRC is invalid
44
45 **/
46 BOOLEAN
47 PartitionCheckGptHeaderCRC (
48 IN EFI_PARTITION_TABLE_HEADER *PartHeader
49 )
50 {
51 UINT32 GptHdrCrc;
52 UINT32 Crc;
53
54 GptHdrCrc = PartHeader->Header.CRC32;
55
56 //
57 // Set CRC field to zero when doing calcuation
58 //
59 PartHeader->Header.CRC32 = 0;
60
61 Crc = CalculateCrc32 (PartHeader, PartHeader->Header.HeaderSize);
62
63 //
64 // Restore Header CRC
65 //
66 PartHeader->Header.CRC32 = GptHdrCrc;
67
68 return (GptHdrCrc == Crc);
69 }
70
71
72 /**
73 Check if the CRC field in the Partition table header is valid
74 for Partition entry array.
75
76 @param[in] PartHeader Partition table header structure
77 @param[in] PartEntry The partition entry array
78
79 @retval TRUE the CRC is valid
80 @retval FALSE the CRC is invalid
81
82 **/
83 BOOLEAN
84 PartitionCheckGptEntryArrayCRC (
85 IN EFI_PARTITION_TABLE_HEADER *PartHeader,
86 IN EFI_PARTITION_ENTRY *PartEntry
87 )
88 {
89 UINT32 Crc;
90 UINTN Size;
91
92 Size = (UINTN)MultU64x32(PartHeader->NumberOfPartitionEntries, PartHeader->SizeOfPartitionEntry);
93 Crc = CalculateCrc32 (PartEntry, Size);
94
95 return (BOOLEAN) (PartHeader->PartitionEntryArrayCRC32 == Crc);
96 }
97
98 /**
99 The function is used for valid GPT table. Both for Primary and Backup GPT header.
100
101 @param[in] PrivateData The global memory map
102 @param[in] ParentBlockDevNo The parent block device
103 @param[in] IsPrimaryHeader Indicate to which header will be checked.
104 @param[in] PartHdr Stores the partition table that is read
105
106 @retval TRUE The partition table is valid
107 @retval FALSE The partition table is not valid
108
109 **/
110 BOOLEAN
111 PartitionCheckGptHeader (
112 IN PEI_FAT_PRIVATE_DATA *PrivateData,
113 IN UINTN ParentBlockDevNo,
114 IN BOOLEAN IsPrimaryHeader,
115 IN EFI_PARTITION_TABLE_HEADER *PartHdr
116 )
117 {
118 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
119 EFI_PEI_LBA Lba;
120 EFI_PEI_LBA AlternateLba;
121 EFI_PEI_LBA EntryArrayLastLba;
122
123 UINT64 PartitionEntryArraySize;
124 UINT64 PartitionEntryBlockNumb;
125 UINT32 EntryArraySizeRemainder;
126
127 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
128
129 if (IsPrimaryHeader) {
130 Lba = PRIMARY_PART_HEADER_LBA;
131 AlternateLba = ParentBlockDev->LastBlock;
132 } else {
133 Lba = ParentBlockDev->LastBlock;
134 AlternateLba = PRIMARY_PART_HEADER_LBA;
135 }
136
137 if ( (PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) ||
138 (PartHdr->Header.Revision != 0x00010000) ||
139 (PartHdr->Header.HeaderSize < 92) ||
140 (PartHdr->Header.HeaderSize > ParentBlockDev->BlockSize) ||
141 (!PartitionCheckGptHeaderCRC (PartHdr)) ||
142 (PartHdr->Header.Reserved != 0)
143 ) {
144 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
145 return FALSE;
146 }
147
148 //
149 // | Block0 | Block1 |Block2 ~ FirstUsableLBA - 1|FirstUsableLBA, ... ,LastUsableLBA|LastUsableLBA+1 ~ LastBlock-1| LastBlock |
150 // |Protective MBR|Primary Header|Entry Array(At Least 16384)| Partition | Entry Array(At Least 16384) |BackUp Header|
151 //
152 // 1. Protective MBR is fixed at Block 0.
153 // 2. Primary Header is fixed at Block 1.
154 // 3. Backup Header is fixed at LastBlock.
155 // 4. Must be remain 128*128 bytes for primary entry array.
156 // 5. Must be remain 128*128 bytes for backup entry array.
157 // 6. SizeOfPartitionEntry must be equals to 128 * 2^n.
158 //
159 if ( (PartHdr->MyLBA != Lba) ||
160 (PartHdr->AlternateLBA != AlternateLba) ||
161 (PartHdr->FirstUsableLBA < 2 + EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) ||
162 (PartHdr->LastUsableLBA > ParentBlockDev->LastBlock - 1 - EFI_SIZE_TO_BLOCKS (EFI_GPT_PART_ENTRY_MIN_SIZE, ParentBlockDev->BlockSize)) ||
163 (PartHdr->FirstUsableLBA > PartHdr->LastUsableLBA) ||
164 (PartHdr->PartitionEntryLBA < 2) ||
165 (PartHdr->PartitionEntryLBA > ParentBlockDev->LastBlock - 1) ||
166 (PartHdr->PartitionEntryLBA >= PartHdr->FirstUsableLBA && PartHdr->PartitionEntryLBA <= PartHdr->LastUsableLBA) ||
167 (PartHdr->SizeOfPartitionEntry%128 != 0) ||
168 (PartHdr->SizeOfPartitionEntry != sizeof (EFI_PARTITION_ENTRY))
169 ) {
170 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
171 return FALSE;
172 }
173
174 //
175 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
176 //
177 if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) {
178 DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n"));
179 return FALSE;
180 }
181
182 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
183 EntryArraySizeRemainder = 0;
184 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
185 if (EntryArraySizeRemainder != 0) {
186 PartitionEntryBlockNumb++;
187 }
188
189 if (IsPrimaryHeader) {
190 EntryArrayLastLba = PartHdr->FirstUsableLBA;
191 } else {
192 EntryArrayLastLba = ParentBlockDev->LastBlock;
193 }
194
195 //
196 // Make sure partition entry array not overlaps with partition area or the LastBlock.
197 //
198 if (PartHdr->PartitionEntryLBA + PartitionEntryBlockNumb > EntryArrayLastLba) {
199 DEBUG ((DEBUG_ERROR, "GPT Partition Entry Array Error!\n"));
200 DEBUG ((DEBUG_ERROR, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize));
201 DEBUG ((DEBUG_ERROR, "PartitionEntryLBA = %lu.\n", PartHdr->PartitionEntryLBA));
202 DEBUG ((DEBUG_ERROR, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb));
203 DEBUG ((DEBUG_ERROR, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba));
204 return FALSE;
205 }
206
207 return TRUE;
208 }
209
210 /**
211 This function is used to verify each partition in block device.
212
213 @param[in] PrivateData The global memory map
214 @param[in] ParentBlockDevNo The parent block device
215 @param[in] PartHdr Stores the partition table that is read
216
217 @retval TRUE The partition is valid
218 @retval FALSE The partition is not valid
219
220 **/
221 BOOLEAN
222 PartitionCheckGptEntryArray (
223 IN PEI_FAT_PRIVATE_DATA *PrivateData,
224 IN UINTN ParentBlockDevNo,
225 IN EFI_PARTITION_TABLE_HEADER *PartHdr
226 )
227 {
228 EFI_STATUS Status;
229 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
230 PEI_FAT_BLOCK_DEVICE *BlockDevPtr;
231
232 UINT64 PartitionEntryArraySize;
233 UINT64 PartitionEntryBlockNumb;
234 UINT32 EntryArraySizeRemainder;
235
236 EFI_PARTITION_ENTRY *PartitionEntryBuffer;
237 EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus;
238
239 BOOLEAN Found;
240 EFI_LBA StartingLBA;
241 EFI_LBA EndingLBA;
242 UINTN Index;
243 UINTN Index1;
244 UINTN Index2;
245 EFI_PARTITION_ENTRY *Entry;
246
247 PartitionEntryBuffer = NULL;
248 PartitionEntryStatus = NULL;
249
250 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
251 Found = FALSE;
252
253 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
254 EntryArraySizeRemainder = 0;
255 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
256 if (EntryArraySizeRemainder != 0) {
257 PartitionEntryBlockNumb++;
258 }
259 PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
260
261 PartitionEntryBuffer = (EFI_PARTITION_ENTRY *) AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
262 if (PartitionEntryBuffer == NULL) {
263 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
264 goto EXIT;
265 }
266
267 PartitionEntryStatus = (EFI_PARTITION_ENTRY_STATUS *) AllocatePages (EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
268 if (PartitionEntryStatus == NULL) {
269 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
270 goto EXIT;
271 }
272 ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
273
274 Status = FatReadBlock (
275 PrivateData,
276 ParentBlockDevNo,
277 PartHdr->PartitionEntryLBA,
278 (UINTN)PartitionEntryArraySize,
279 PartitionEntryBuffer
280 );
281 if (EFI_ERROR (Status)) {
282 DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n"));
283 goto EXIT;
284 }
285
286 if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) {
287 DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n"));
288 goto EXIT;
289 }
290
291 for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) {
292 Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry);
293 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
294 continue;
295 }
296
297 StartingLBA = Entry->StartingLBA;
298 EndingLBA = Entry->EndingLBA;
299 if (StartingLBA > EndingLBA ||
300 StartingLBA < PartHdr->FirstUsableLBA ||
301 StartingLBA > PartHdr->LastUsableLBA ||
302 EndingLBA < PartHdr->FirstUsableLBA ||
303 EndingLBA > PartHdr->LastUsableLBA
304 ) {
305 PartitionEntryStatus[Index1].OutOfRange = TRUE;
306 continue;
307 }
308
309 if ((Entry->Attributes & BIT1) != 0) {
310 //
311 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
312 //
313 PartitionEntryStatus[Index1].OsSpecific = TRUE;
314 }
315
316 for (Index2 = Index1 + 1; Index2 < PartHdr->NumberOfPartitionEntries; Index2++) {
317 Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index2 * PartHdr->SizeOfPartitionEntry);
318 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
319 continue;
320 }
321
322 if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) {
323 //
324 // This region overlaps with the Index1'th region
325 //
326 PartitionEntryStatus[Index1].Overlap = TRUE;
327 PartitionEntryStatus[Index2].Overlap = TRUE;
328 continue;
329 }
330 }
331 }
332
333 for (Index = 0; Index < PartHdr->NumberOfPartitionEntries; Index++) {
334 if (CompareGuid (&PartitionEntryBuffer[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid)||
335 PartitionEntryStatus[Index].OutOfRange ||
336 PartitionEntryStatus[Index].Overlap ||
337 PartitionEntryStatus[Index].OsSpecific) {
338 //
339 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
340 // partition Entries
341 //
342 continue;
343 }
344
345 if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
346 break;
347 }
348
349 Found = TRUE;
350 BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
351
352 BlockDevPtr->BlockSize = ParentBlockDev->BlockSize;
353 BlockDevPtr->LastBlock = PartitionEntryBuffer[Index].EndingLBA;
354 BlockDevPtr->IoAlign = ParentBlockDev->IoAlign;
355 BlockDevPtr->Logical = TRUE;
356 BlockDevPtr->PartitionChecked = FALSE;
357 BlockDevPtr->StartingPos = MultU64x32 (
358 PartitionEntryBuffer[Index].StartingLBA,
359 ParentBlockDev->BlockSize
360 );
361 BlockDevPtr->ParentDevNo = ParentBlockDevNo;
362
363 PrivateData->BlockDeviceCount++;
364
365 DEBUG ((DEBUG_INFO, "Find GPT Partition [0x%lx", PartitionEntryBuffer[Index].StartingLBA, BlockDevPtr->LastBlock));
366 DEBUG ((DEBUG_INFO, ", 0x%lx]\n", BlockDevPtr->LastBlock));
367 DEBUG ((DEBUG_INFO, " BlockSize %x\n", BlockDevPtr->BlockSize));
368 }
369
370 EXIT:
371 if (PartitionEntryBuffer != NULL) {
372 FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
373 }
374
375 if (PartitionEntryStatus != NULL) {
376 FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
377 }
378
379 return Found;
380 }
381
382 /**
383 The function is used to check GPT structure, include GPT header and GPT entry array.
384
385 1. Check GPT header.
386 2. Check partition entry array.
387 3. Check each partitions.
388
389 @param[in] PrivateData The global memory map
390 @param[in] ParentBlockDevNo The parent block device
391 @param[in] IsPrimary Indicate primary or backup to be check
392
393 @retval TRUE Primary or backup GPT structure is valid.
394 @retval FALSE Both primary and backup are invalid.
395
396 **/
397 BOOLEAN
398 PartitionCheckGptStructure (
399 IN PEI_FAT_PRIVATE_DATA *PrivateData,
400 IN UINTN ParentBlockDevNo,
401 IN BOOLEAN IsPrimary
402 )
403 {
404 EFI_STATUS Status;
405 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
406 EFI_PARTITION_TABLE_HEADER *PartHdr;
407 EFI_PEI_LBA GptHeaderLBA;
408
409 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
410 PartHdr = (EFI_PARTITION_TABLE_HEADER *) PrivateData->BlockData;
411
412 if (IsPrimary) {
413 GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
414 } else {
415 GptHeaderLBA = ParentBlockDev->LastBlock;
416 }
417
418 Status = FatReadBlock (
419 PrivateData,
420 ParentBlockDevNo,
421 GptHeaderLBA,
422 ParentBlockDev->BlockSize,
423 PartHdr
424 );
425 if (EFI_ERROR (Status)) {
426 return FALSE;
427 }
428
429 if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
430 return FALSE;
431 }
432
433 if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
434 return FALSE;
435 }
436
437 return TRUE;
438 }
439
440 /**
441 This function is used to check protective MBR structure before checking GPT.
442
443 @param[in] PrivateData The global memory map
444 @param[in] ParentBlockDevNo The parent block device
445
446 @retval TRUE Valid protective MBR
447 @retval FALSE Invalid MBR
448 **/
449 BOOLEAN
450 PartitionCheckProtectiveMbr (
451 IN PEI_FAT_PRIVATE_DATA *PrivateData,
452 IN UINTN ParentBlockDevNo
453 )
454 {
455 EFI_STATUS Status;
456 MASTER_BOOT_RECORD *ProtectiveMbr;
457 MBR_PARTITION_RECORD *MbrPartition;
458 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
459 UINTN Index;
460
461 ProtectiveMbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData;
462 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
463
464 //
465 // Read Protective MBR
466 //
467 Status = FatReadBlock (
468 PrivateData,
469 ParentBlockDevNo,
470 0,
471 ParentBlockDev->BlockSize,
472 ProtectiveMbr
473 );
474 if (EFI_ERROR (Status)) {
475 DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
476 return FALSE;
477 }
478
479 if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
480 DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
481 return FALSE;
482 }
483
484 //
485 // The partition define in UEFI Spec Table 17.
486 // Boot Code, Unique MBR Disk Signature, Unknown.
487 // These parts will not be used by UEFI, so we skip to check them.
488 //
489 for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
490 MbrPartition = (MBR_PARTITION_RECORD *)&ProtectiveMbr->Partition[Index];
491 if (MbrPartition->BootIndicator == 0x00 &&
492 MbrPartition->StartSector == 0x02 &&
493 MbrPartition->OSIndicator == PMBR_GPT_PARTITION &&
494 UNPACK_UINT32 (MbrPartition->StartingLBA) == 1
495 ) {
496 return TRUE;
497 }
498 }
499
500 DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n"));
501 return FALSE;
502 }
503
504 /**
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.
508
509 @param[in] PrivateData The global memory map
510 @param[in] ParentBlockDevNo The parent block device
511
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
515
516 **/
517 BOOLEAN
518 FatFindGptPartitions (
519 IN PEI_FAT_PRIVATE_DATA *PrivateData,
520 IN UINTN ParentBlockDevNo
521 )
522 {
523 BOOLEAN Found;
524 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
525
526 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
527 return FALSE;
528 }
529
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));
533 return FALSE;
534 }
535
536 if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) {
537 return FALSE;
538 }
539
540 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE);
541 if (!Found) {
542 DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
543 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE);
544 }
545
546 if (Found) {
547 ParentBlockDev->PartitionChecked = TRUE;
548 }
549
550 return Found;
551 }