]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/FatPei/Gpt.c
FatPkg: Add GPT check in FatPei to support Capsule-on-Disk feature.
[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 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
248 Found = FALSE;
249
250 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
251 EntryArraySizeRemainder = 0;
252 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
253 if (EntryArraySizeRemainder != 0) {
254 PartitionEntryBlockNumb++;
255 }
256 PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
257
258 PartitionEntryBuffer = (EFI_PARTITION_ENTRY *) AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
259 if (PartitionEntryBuffer == NULL) {
260 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
261 goto EXIT;
262 }
263
264 PartitionEntryStatus = (EFI_PARTITION_ENTRY_STATUS *) AllocatePages (EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
265 if (PartitionEntryStatus == NULL) {
266 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
267 goto EXIT;
268 }
269 ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
270
271 Status = FatReadBlock (
272 PrivateData,
273 ParentBlockDevNo,
274 PartHdr->PartitionEntryLBA,
275 (UINTN)PartitionEntryArraySize,
276 PartitionEntryBuffer
277 );
278 if (EFI_ERROR (Status)) {
279 DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n"));
280 goto EXIT;
281 }
282
283 if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) {
284 DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n"));
285 goto EXIT;
286 }
287
288 for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) {
289 Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry);
290 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
291 continue;
292 }
293
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
301 ) {
302 PartitionEntryStatus[Index1].OutOfRange = TRUE;
303 continue;
304 }
305
306 if ((Entry->Attributes & BIT1) != 0) {
307 //
308 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
309 //
310 PartitionEntryStatus[Index1].OsSpecific = TRUE;
311 }
312
313 for (Index2 = Index1 + 1; Index2 < PartHdr->NumberOfPartitionEntries; Index2++) {
314 Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartitionEntryBuffer + Index2 * PartHdr->SizeOfPartitionEntry);
315 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
316 continue;
317 }
318
319 if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) {
320 //
321 // This region overlaps with the Index1'th region
322 //
323 PartitionEntryStatus[Index1].Overlap = TRUE;
324 PartitionEntryStatus[Index2].Overlap = TRUE;
325 continue;
326 }
327 }
328 }
329
330 for (Index = 0; Index < PartHdr->NumberOfPartitionEntries; Index++) {
331 if (CompareGuid (&PartitionEntryBuffer[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid)||
332 PartitionEntryStatus[Index].OutOfRange ||
333 PartitionEntryStatus[Index].Overlap ||
334 PartitionEntryStatus[Index].OsSpecific) {
335 //
336 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
337 // partition Entries
338 //
339 continue;
340 }
341
342 if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
343 break;
344 }
345
346 Found = TRUE;
347 BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
348
349 BlockDevPtr->BlockSize = ParentBlockDev->BlockSize;
350 BlockDevPtr->LastBlock = PartitionEntryBuffer[Index].EndingLBA;
351 BlockDevPtr->IoAlign = ParentBlockDev->IoAlign;
352 BlockDevPtr->Logical = TRUE;
353 BlockDevPtr->PartitionChecked = FALSE;
354 BlockDevPtr->StartingPos = MultU64x32 (
355 PartitionEntryBuffer[Index].StartingLBA,
356 ParentBlockDev->BlockSize
357 );
358 BlockDevPtr->ParentDevNo = ParentBlockDevNo;
359
360 PrivateData->BlockDeviceCount++;
361
362 DEBUG ((DEBUG_INFO, "Find GPT Partition [0x%lx", PartitionEntryBuffer[Index].StartingLBA, BlockDevPtr->LastBlock));
363 DEBUG ((DEBUG_INFO, ", 0x%lx]\n", BlockDevPtr->LastBlock));
364 DEBUG ((DEBUG_INFO, " BlockSize %x\n", BlockDevPtr->BlockSize));
365 }
366
367 EXIT:
368 if (PartitionEntryBuffer != NULL) {
369 FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
370 }
371
372 if (PartitionEntryStatus != NULL) {
373 FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
374 }
375
376 return Found;
377 }
378
379 /**
380 The function is used to check GPT structure, include GPT header and GPT entry array.
381
382 1. Check GPT header.
383 2. Check partition entry array.
384 3. Check each partitions.
385
386 @param[in] PrivateData The global memory map
387 @param[in] ParentBlockDevNo The parent block device
388 @param[in] IsPrimary Indicate primary or backup to be check
389
390 @retval TRUE Primary or backup GPT structure is valid.
391 @retval FALSE Both primary and backup are invalid.
392
393 **/
394 BOOLEAN
395 PartitionCheckGptStructure (
396 IN PEI_FAT_PRIVATE_DATA *PrivateData,
397 IN UINTN ParentBlockDevNo,
398 IN BOOLEAN IsPrimary
399 )
400 {
401 EFI_STATUS Status;
402 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
403 EFI_PARTITION_TABLE_HEADER *PartHdr;
404 EFI_PEI_LBA GptHeaderLBA;
405
406 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
407 PartHdr = (EFI_PARTITION_TABLE_HEADER *) PrivateData->BlockData;
408
409 if (IsPrimary) {
410 GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
411 } else {
412 GptHeaderLBA = ParentBlockDev->LastBlock;
413 }
414
415 Status = FatReadBlock (
416 PrivateData,
417 ParentBlockDevNo,
418 GptHeaderLBA,
419 ParentBlockDev->BlockSize,
420 PartHdr
421 );
422 if (EFI_ERROR (Status)) {
423 return FALSE;
424 }
425
426 if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
427 return FALSE;
428 }
429
430 if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
431 return FALSE;
432 }
433
434 return TRUE;
435 }
436
437 /**
438 This function is used to check protective MBR structure before checking GPT.
439
440 @param[in] PrivateData The global memory map
441 @param[in] ParentBlockDevNo The parent block device
442
443 @retval TRUE Valid protective MBR
444 @retval FALSE Invalid MBR
445 **/
446 BOOLEAN
447 PartitionCheckProtectiveMbr (
448 IN PEI_FAT_PRIVATE_DATA *PrivateData,
449 IN UINTN ParentBlockDevNo
450 )
451 {
452 EFI_STATUS Status;
453 MASTER_BOOT_RECORD *ProtectiveMbr;
454 MBR_PARTITION_RECORD *MbrPartition;
455 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
456 UINTN Index;
457
458 ProtectiveMbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData;
459 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
460
461 //
462 // Read Protective MBR
463 //
464 Status = FatReadBlock (
465 PrivateData,
466 ParentBlockDevNo,
467 0,
468 ParentBlockDev->BlockSize,
469 ProtectiveMbr
470 );
471 if (EFI_ERROR (Status)) {
472 DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
473 return FALSE;
474 }
475
476 if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
477 DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
478 return FALSE;
479 }
480
481 //
482 // The partition define in UEFI Spec Table 17.
483 // Boot Code, Unique MBR Disk Signature, Unknown.
484 // These parts will not be used by UEFI, so we skip to check them.
485 //
486 for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
487 MbrPartition = (MBR_PARTITION_RECORD *)&ProtectiveMbr->Partition[Index];
488 if (MbrPartition->BootIndicator == 0x00 &&
489 MbrPartition->StartSector == 0x02 &&
490 MbrPartition->OSIndicator == PMBR_GPT_PARTITION &&
491 UNPACK_UINT32 (MbrPartition->StartingLBA) == 1
492 ) {
493 return TRUE;
494 }
495 }
496
497 DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n"));
498 return FALSE;
499 }
500
501 /**
502 This function is used for finding GPT partition on block device.
503 As follow UEFI spec we should check protective MBR first and then
504 try to check both primary/backup GPT structures.
505
506 @param[in] PrivateData The global memory map
507 @param[in] ParentBlockDevNo The parent block device
508
509 @retval TRUE New partitions are detected and logical block devices
510 are added to block device array
511 @retval FALSE No new partitions are added
512
513 **/
514 BOOLEAN
515 FatFindGptPartitions (
516 IN PEI_FAT_PRIVATE_DATA *PrivateData,
517 IN UINTN ParentBlockDevNo
518 )
519 {
520 BOOLEAN Found;
521 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
522
523 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
524 return FALSE;
525 }
526
527 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
528 if (ParentBlockDev->BlockSize > PEI_FAT_MAX_BLOCK_SIZE) {
529 DEBUG ((DEBUG_ERROR, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev->BlockSize));
530 return FALSE;
531 }
532
533 if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) {
534 return FALSE;
535 }
536
537 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE);
538 if (!Found) {
539 DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
540 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE);
541 }
542
543 if (Found) {
544 ParentBlockDev->PartitionChecked = TRUE;
545 }
546
547 return Found;
548 }