]> git.proxmox.com Git - mirror_edk2.git/blob - FatPkg/FatPei/Gpt.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 calculation
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 Check if the CRC field in the Partition table header is valid
67 for Partition entry array.
68
69 @param[in] PartHeader Partition table header structure
70 @param[in] PartEntry The partition entry array
71
72 @retval TRUE the CRC is valid
73 @retval FALSE the CRC is invalid
74
75 **/
76 BOOLEAN
77 PartitionCheckGptEntryArrayCRC (
78 IN EFI_PARTITION_TABLE_HEADER *PartHeader,
79 IN EFI_PARTITION_ENTRY *PartEntry
80 )
81 {
82 UINT32 Crc;
83 UINTN Size;
84
85 Size = (UINTN)MultU64x32 (PartHeader->NumberOfPartitionEntries, PartHeader->SizeOfPartitionEntry);
86 Crc = CalculateCrc32 (PartEntry, Size);
87
88 return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc);
89 }
90
91 /**
92 The function is used for valid GPT table. Both for Primary and Backup GPT header.
93
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
98
99 @retval TRUE The partition table is valid
100 @retval FALSE The partition table is not valid
101
102 **/
103 BOOLEAN
104 PartitionCheckGptHeader (
105 IN PEI_FAT_PRIVATE_DATA *PrivateData,
106 IN UINTN ParentBlockDevNo,
107 IN BOOLEAN IsPrimaryHeader,
108 IN EFI_PARTITION_TABLE_HEADER *PartHdr
109 )
110 {
111 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
112 EFI_PEI_LBA Lba;
113 EFI_PEI_LBA AlternateLba;
114 EFI_PEI_LBA EntryArrayLastLba;
115
116 UINT64 PartitionEntryArraySize;
117 UINT64 PartitionEntryBlockNumb;
118 UINT32 EntryArraySizeRemainder;
119
120 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
121
122 if (IsPrimaryHeader) {
123 Lba = PRIMARY_PART_HEADER_LBA;
124 AlternateLba = ParentBlockDev->LastBlock;
125 } else {
126 Lba = ParentBlockDev->LastBlock;
127 AlternateLba = PRIMARY_PART_HEADER_LBA;
128 }
129
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)
136 )
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 {
165 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
166 return FALSE;
167 }
168
169 //
170 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
171 //
172 if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) {
173 DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n"));
174 return FALSE;
175 }
176
177 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
178 EntryArraySizeRemainder = 0;
179 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
180 if (EntryArraySizeRemainder != 0) {
181 PartitionEntryBlockNumb++;
182 }
183
184 if (IsPrimaryHeader) {
185 EntryArrayLastLba = PartHdr->FirstUsableLBA;
186 } else {
187 EntryArrayLastLba = ParentBlockDev->LastBlock;
188 }
189
190 //
191 // Make sure partition entry array not overlaps with partition area or the LastBlock.
192 //
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));
199 return FALSE;
200 }
201
202 return TRUE;
203 }
204
205 /**
206 This function is used to verify each partition in block device.
207
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
211
212 @retval TRUE The partition is valid
213 @retval FALSE The partition is not valid
214
215 **/
216 BOOLEAN
217 PartitionCheckGptEntryArray (
218 IN PEI_FAT_PRIVATE_DATA *PrivateData,
219 IN UINTN ParentBlockDevNo,
220 IN EFI_PARTITION_TABLE_HEADER *PartHdr
221 )
222 {
223 EFI_STATUS Status;
224 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
225 PEI_FAT_BLOCK_DEVICE *BlockDevPtr;
226
227 UINT64 PartitionEntryArraySize;
228 UINT64 PartitionEntryBlockNumb;
229 UINT32 EntryArraySizeRemainder;
230
231 EFI_PARTITION_ENTRY *PartitionEntryBuffer;
232 EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus;
233
234 BOOLEAN Found;
235 EFI_LBA StartingLBA;
236 EFI_LBA EndingLBA;
237 UINTN Index;
238 UINTN Index1;
239 UINTN Index2;
240 EFI_PARTITION_ENTRY *Entry;
241
242 PartitionEntryBuffer = NULL;
243 PartitionEntryStatus = NULL;
244
245 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
246 Found = FALSE;
247
248 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
249 EntryArraySizeRemainder = 0;
250 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
251 if (EntryArraySizeRemainder != 0) {
252 PartitionEntryBlockNumb++;
253 }
254
255 PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
256
257 PartitionEntryBuffer = (EFI_PARTITION_ENTRY *)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
258 if (PartitionEntryBuffer == NULL) {
259 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
260 goto EXIT;
261 }
262
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"));
266 goto EXIT;
267 }
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 {
303 PartitionEntryStatus[Index1].OutOfRange = TRUE;
304 continue;
305 }
306
307 if ((Entry->Attributes & BIT1) != 0) {
308 //
309 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
310 //
311 PartitionEntryStatus[Index1].OsSpecific = TRUE;
312 }
313
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)) {
317 continue;
318 }
319
320 if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) {
321 //
322 // This region overlaps with the Index1'th region
323 //
324 PartitionEntryStatus[Index1].Overlap = TRUE;
325 PartitionEntryStatus[Index2].Overlap = TRUE;
326 continue;
327 }
328 }
329 }
330
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)
336 {
337 //
338 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
339 // partition Entries
340 //
341 continue;
342 }
343
344 if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
345 break;
346 }
347
348 Found = TRUE;
349 BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
350
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
359 );
360 BlockDevPtr->ParentDevNo = ParentBlockDevNo;
361
362 PrivateData->BlockDeviceCount++;
363
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));
367 }
368
369 EXIT:
370 if (PartitionEntryBuffer != NULL) {
371 FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
372 }
373
374 if (PartitionEntryStatus != NULL) {
375 FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
376 }
377
378 return Found;
379 }
380
381 /**
382 The function is used to check GPT structure, include GPT header and GPT entry array.
383
384 1. Check GPT header.
385 2. Check partition entry array.
386 3. Check each partitions.
387
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
391
392 @retval TRUE Primary or backup GPT structure is valid.
393 @retval FALSE Both primary and backup are invalid.
394
395 **/
396 BOOLEAN
397 PartitionCheckGptStructure (
398 IN PEI_FAT_PRIVATE_DATA *PrivateData,
399 IN UINTN ParentBlockDevNo,
400 IN BOOLEAN IsPrimary
401 )
402 {
403 EFI_STATUS Status;
404 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
405 EFI_PARTITION_TABLE_HEADER *PartHdr;
406 EFI_PEI_LBA GptHeaderLBA;
407
408 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
409 PartHdr = (EFI_PARTITION_TABLE_HEADER *)PrivateData->BlockData;
410
411 if (IsPrimary) {
412 GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
413 } else {
414 GptHeaderLBA = ParentBlockDev->LastBlock;
415 }
416
417 Status = FatReadBlock (
418 PrivateData,
419 ParentBlockDevNo,
420 GptHeaderLBA,
421 ParentBlockDev->BlockSize,
422 PartHdr
423 );
424 if (EFI_ERROR (Status)) {
425 return FALSE;
426 }
427
428 if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
429 return FALSE;
430 }
431
432 if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
433 return FALSE;
434 }
435
436 return TRUE;
437 }
438
439 /**
440 This function is used to check protective MBR structure before checking GPT.
441
442 @param[in] PrivateData The global memory map
443 @param[in] ParentBlockDevNo The parent block device
444
445 @retval TRUE Valid protective MBR
446 @retval FALSE Invalid MBR
447 **/
448 BOOLEAN
449 PartitionCheckProtectiveMbr (
450 IN PEI_FAT_PRIVATE_DATA *PrivateData,
451 IN UINTN ParentBlockDevNo
452 )
453 {
454 EFI_STATUS Status;
455 MASTER_BOOT_RECORD *ProtectiveMbr;
456 MBR_PARTITION_RECORD *MbrPartition;
457 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
458 UINTN Index;
459
460 ProtectiveMbr = (MASTER_BOOT_RECORD *)PrivateData->BlockData;
461 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
462
463 //
464 // Read Protective MBR
465 //
466 Status = FatReadBlock (
467 PrivateData,
468 ParentBlockDevNo,
469 0,
470 ParentBlockDev->BlockSize,
471 ProtectiveMbr
472 );
473 if (EFI_ERROR (Status)) {
474 DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
475 return FALSE;
476 }
477
478 if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
479 DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
480 return FALSE;
481 }
482
483 //
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.
487 //
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)
494 )
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 }