VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/FatPkg/FatPei/Gpt.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 15.5 KB
Line 
1/** @file
2 Routines supporting partition discovery and
3 logical device reading
4
5Copyright (c) 2019 Intel Corporation. All rights reserved.<BR>
6
7SPDX-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//
25typedef 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**/
40BOOLEAN
41PartitionCheckGptHeaderCRC (
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**/
76BOOLEAN
77PartitionCheckGptEntryArrayCRC (
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**/
103BOOLEAN
104PartitionCheckGptHeader (
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 EntryArrayLastLba;
114
115 UINT64 PartitionEntryArraySize;
116 UINT64 PartitionEntryBlockNumb;
117 UINT32 EntryArraySizeRemainder;
118
119 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
120
121 if (IsPrimaryHeader) {
122 Lba = PRIMARY_PART_HEADER_LBA;
123 } else {
124 Lba = ParentBlockDev->LastBlock;
125 }
126
127 if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) ||
128 (!PartitionCheckGptHeaderCRC (PartHdr)) ||
129 (PartHdr->MyLBA != Lba) ||
130 (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY))
131 )
132 {
133 DEBUG ((DEBUG_ERROR, "Invalid efi partition table header\n"));
134 return FALSE;
135 }
136
137 //
138 // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow.
139 //
140 if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) {
141 DEBUG ((DEBUG_ERROR, "Memory overflow in GPT Entry Array\n"));
142 return FALSE;
143 }
144
145 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
146 EntryArraySizeRemainder = 0;
147 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
148 if (EntryArraySizeRemainder != 0) {
149 PartitionEntryBlockNumb++;
150 }
151
152 if (IsPrimaryHeader) {
153 EntryArrayLastLba = PartHdr->FirstUsableLBA;
154 } else {
155 EntryArrayLastLba = ParentBlockDev->LastBlock;
156 }
157
158 //
159 // Make sure partition entry array not overlaps with partition area or the LastBlock.
160 //
161 if (PartHdr->PartitionEntryLBA + PartitionEntryBlockNumb > EntryArrayLastLba) {
162 DEBUG ((DEBUG_ERROR, "GPT Partition Entry Array Error!\n"));
163 DEBUG ((DEBUG_ERROR, "PartitionEntryArraySize = %lu.\n", PartitionEntryArraySize));
164 DEBUG ((DEBUG_ERROR, "PartitionEntryLBA = %lu.\n", PartHdr->PartitionEntryLBA));
165 DEBUG ((DEBUG_ERROR, "PartitionEntryBlockNumb = %lu.\n", PartitionEntryBlockNumb));
166 DEBUG ((DEBUG_ERROR, "EntryArrayLastLba = %lu.\n", EntryArrayLastLba));
167 return FALSE;
168 }
169
170 return TRUE;
171}
172
173/**
174 This function is used to verify each partition in block device.
175
176 @param[in] PrivateData The global memory map
177 @param[in] ParentBlockDevNo The parent block device
178 @param[in] PartHdr Stores the partition table that is read
179
180 @retval TRUE The partition is valid
181 @retval FALSE The partition is not valid
182
183**/
184BOOLEAN
185PartitionCheckGptEntryArray (
186 IN PEI_FAT_PRIVATE_DATA *PrivateData,
187 IN UINTN ParentBlockDevNo,
188 IN EFI_PARTITION_TABLE_HEADER *PartHdr
189 )
190{
191 EFI_STATUS Status;
192 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
193 PEI_FAT_BLOCK_DEVICE *BlockDevPtr;
194
195 UINT64 PartitionEntryArraySize;
196 UINT64 PartitionEntryBlockNumb;
197 UINT32 EntryArraySizeRemainder;
198
199 EFI_PARTITION_ENTRY *PartitionEntryBuffer;
200 EFI_PARTITION_ENTRY_STATUS *PartitionEntryStatus;
201
202 BOOLEAN Found;
203 EFI_LBA StartingLBA;
204 EFI_LBA EndingLBA;
205 UINTN Index;
206 UINTN Index1;
207 UINTN Index2;
208 EFI_PARTITION_ENTRY *Entry;
209
210 PartitionEntryBuffer = NULL;
211 PartitionEntryStatus = NULL;
212
213 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
214 Found = FALSE;
215
216 PartitionEntryArraySize = MultU64x32 (PartHdr->NumberOfPartitionEntries, PartHdr->SizeOfPartitionEntry);
217 EntryArraySizeRemainder = 0;
218 PartitionEntryBlockNumb = DivU64x32Remainder (PartitionEntryArraySize, ParentBlockDev->BlockSize, &EntryArraySizeRemainder);
219 if (EntryArraySizeRemainder != 0) {
220 PartitionEntryBlockNumb++;
221 }
222
223 PartitionEntryArraySize = MultU64x32 (PartitionEntryBlockNumb, ParentBlockDev->BlockSize);
224
225 PartitionEntryBuffer = (EFI_PARTITION_ENTRY *)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
226 if (PartitionEntryBuffer == NULL) {
227 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
228 goto EXIT;
229 }
230
231 PartitionEntryStatus = (EFI_PARTITION_ENTRY_STATUS *)AllocatePages (EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
232 if (PartitionEntryStatus == NULL) {
233 DEBUG ((DEBUG_ERROR, "Allocate memory error!\n"));
234 goto EXIT;
235 }
236
237 ZeroMem (PartitionEntryStatus, PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS));
238
239 Status = FatReadBlock (
240 PrivateData,
241 ParentBlockDevNo,
242 PartHdr->PartitionEntryLBA,
243 (UINTN)PartitionEntryArraySize,
244 PartitionEntryBuffer
245 );
246 if (EFI_ERROR (Status)) {
247 DEBUG ((DEBUG_ERROR, "Read partition entry array error!\n"));
248 goto EXIT;
249 }
250
251 if (!PartitionCheckGptEntryArrayCRC (PartHdr, PartitionEntryBuffer)) {
252 DEBUG ((DEBUG_ERROR, "Partition entries CRC check fail\n"));
253 goto EXIT;
254 }
255
256 for (Index1 = 0; Index1 < PartHdr->NumberOfPartitionEntries; Index1++) {
257 Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntryBuffer + Index1 * PartHdr->SizeOfPartitionEntry);
258 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
259 continue;
260 }
261
262 StartingLBA = Entry->StartingLBA;
263 EndingLBA = Entry->EndingLBA;
264 if ((StartingLBA > EndingLBA) ||
265 (StartingLBA < PartHdr->FirstUsableLBA) ||
266 (StartingLBA > PartHdr->LastUsableLBA) ||
267 (EndingLBA < PartHdr->FirstUsableLBA) ||
268 (EndingLBA > PartHdr->LastUsableLBA)
269 )
270 {
271 PartitionEntryStatus[Index1].OutOfRange = TRUE;
272 continue;
273 }
274
275 if ((Entry->Attributes & BIT1) != 0) {
276 //
277 // If Bit 1 is set, this indicate that this is an OS specific GUID partition.
278 //
279 PartitionEntryStatus[Index1].OsSpecific = TRUE;
280 }
281
282 for (Index2 = Index1 + 1; Index2 < PartHdr->NumberOfPartitionEntries; Index2++) {
283 Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntryBuffer + Index2 * PartHdr->SizeOfPartitionEntry);
284 if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) {
285 continue;
286 }
287
288 if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) {
289 //
290 // This region overlaps with the Index1'th region
291 //
292 PartitionEntryStatus[Index1].Overlap = TRUE;
293 PartitionEntryStatus[Index2].Overlap = TRUE;
294 continue;
295 }
296 }
297 }
298
299 for (Index = 0; Index < PartHdr->NumberOfPartitionEntries; Index++) {
300 if (CompareGuid (&PartitionEntryBuffer[Index].PartitionTypeGUID, &gEfiPartTypeUnusedGuid) ||
301 PartitionEntryStatus[Index].OutOfRange ||
302 PartitionEntryStatus[Index].Overlap ||
303 PartitionEntryStatus[Index].OsSpecific)
304 {
305 //
306 // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific
307 // partition Entries
308 //
309 continue;
310 }
311
312 if (PrivateData->BlockDeviceCount >= PEI_FAT_MAX_BLOCK_DEVICE) {
313 break;
314 }
315
316 Found = TRUE;
317 BlockDevPtr = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
318
319 BlockDevPtr->BlockSize = ParentBlockDev->BlockSize;
320 BlockDevPtr->LastBlock = PartitionEntryBuffer[Index].EndingLBA;
321 BlockDevPtr->IoAlign = ParentBlockDev->IoAlign;
322 BlockDevPtr->Logical = TRUE;
323 BlockDevPtr->PartitionChecked = FALSE;
324 BlockDevPtr->StartingPos = MultU64x32 (
325 PartitionEntryBuffer[Index].StartingLBA,
326 ParentBlockDev->BlockSize
327 );
328 BlockDevPtr->ParentDevNo = ParentBlockDevNo;
329
330 PrivateData->BlockDeviceCount++;
331
332 DEBUG ((DEBUG_INFO, "Find GPT Partition [0x%lx", PartitionEntryBuffer[Index].StartingLBA));
333 DEBUG ((DEBUG_INFO, ", 0x%lx]\n", BlockDevPtr->LastBlock));
334 DEBUG ((DEBUG_INFO, " BlockSize %x\n", BlockDevPtr->BlockSize));
335 }
336
337EXIT:
338 if (PartitionEntryBuffer != NULL) {
339 FreePages (PartitionEntryBuffer, EFI_SIZE_TO_PAGES ((UINTN)PartitionEntryArraySize));
340 }
341
342 if (PartitionEntryStatus != NULL) {
343 FreePages (PartitionEntryStatus, EFI_SIZE_TO_PAGES (PartHdr->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)));
344 }
345
346 return Found;
347}
348
349/**
350 The function is used to check GPT structure, include GPT header and GPT entry array.
351
352 1. Check GPT header.
353 2. Check partition entry array.
354 3. Check each partitions.
355
356 @param[in] PrivateData The global memory map
357 @param[in] ParentBlockDevNo The parent block device
358 @param[in] IsPrimary Indicate primary or backup to be check
359
360 @retval TRUE Primary or backup GPT structure is valid.
361 @retval FALSE Both primary and backup are invalid.
362
363**/
364BOOLEAN
365PartitionCheckGptStructure (
366 IN PEI_FAT_PRIVATE_DATA *PrivateData,
367 IN UINTN ParentBlockDevNo,
368 IN BOOLEAN IsPrimary
369 )
370{
371 EFI_STATUS Status;
372 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
373 EFI_PARTITION_TABLE_HEADER *PartHdr;
374 EFI_PEI_LBA GptHeaderLBA;
375
376 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
377 PartHdr = (EFI_PARTITION_TABLE_HEADER *)PrivateData->BlockData;
378
379 if (IsPrimary) {
380 GptHeaderLBA = PRIMARY_PART_HEADER_LBA;
381 } else {
382 GptHeaderLBA = ParentBlockDev->LastBlock;
383 }
384
385 Status = FatReadBlock (
386 PrivateData,
387 ParentBlockDevNo,
388 GptHeaderLBA,
389 ParentBlockDev->BlockSize,
390 PartHdr
391 );
392 if (EFI_ERROR (Status)) {
393 return FALSE;
394 }
395
396 if (!PartitionCheckGptHeader (PrivateData, ParentBlockDevNo, IsPrimary, PartHdr)) {
397 return FALSE;
398 }
399
400 if (!PartitionCheckGptEntryArray (PrivateData, ParentBlockDevNo, PartHdr)) {
401 return FALSE;
402 }
403
404 return TRUE;
405}
406
407/**
408 This function is used to check protective MBR structure before checking GPT.
409
410 @param[in] PrivateData The global memory map
411 @param[in] ParentBlockDevNo The parent block device
412
413 @retval TRUE Valid protective MBR
414 @retval FALSE Invalid MBR
415**/
416BOOLEAN
417PartitionCheckProtectiveMbr (
418 IN PEI_FAT_PRIVATE_DATA *PrivateData,
419 IN UINTN ParentBlockDevNo
420 )
421{
422 EFI_STATUS Status;
423 MASTER_BOOT_RECORD *ProtectiveMbr;
424 MBR_PARTITION_RECORD *MbrPartition;
425 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
426 UINTN Index;
427
428 ProtectiveMbr = (MASTER_BOOT_RECORD *)PrivateData->BlockData;
429 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
430
431 //
432 // Read Protective MBR
433 //
434 Status = FatReadBlock (
435 PrivateData,
436 ParentBlockDevNo,
437 0,
438 ParentBlockDev->BlockSize,
439 ProtectiveMbr
440 );
441 if (EFI_ERROR (Status)) {
442 DEBUG ((DEBUG_ERROR, "GPT Error When Read Protective Mbr From Partition!\n"));
443 return FALSE;
444 }
445
446 if (ProtectiveMbr->Signature != MBR_SIGNATURE) {
447 DEBUG ((DEBUG_ERROR, "Protective Mbr Signature is invalid!\n"));
448 return FALSE;
449 }
450
451 //
452 // The partition define in UEFI Spec Table 17.
453 // Boot Code, Unique MBR Disk Signature, Unknown.
454 // These parts will not be used by UEFI, so we skip to check them.
455 //
456 for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
457 MbrPartition = (MBR_PARTITION_RECORD *)&ProtectiveMbr->Partition[Index];
458 if ((MbrPartition->BootIndicator == 0x00) &&
459 (MbrPartition->StartSector == 0x02) &&
460 (MbrPartition->OSIndicator == PMBR_GPT_PARTITION) &&
461 (UNPACK_UINT32 (MbrPartition->StartingLBA) == 1)
462 )
463 {
464 return TRUE;
465 }
466 }
467
468 DEBUG ((DEBUG_ERROR, "Protective Mbr, All Partition Entry Are Empty!\n"));
469 return FALSE;
470}
471
472/**
473 This function is used for finding GPT partition on block device.
474 As follow UEFI spec we should check protective MBR first and then
475 try to check both primary/backup GPT structures.
476
477 @param[in] PrivateData The global memory map
478 @param[in] ParentBlockDevNo The parent block device
479
480 @retval TRUE New partitions are detected and logical block devices
481 are added to block device array
482 @retval FALSE No new partitions are added
483
484**/
485BOOLEAN
486FatFindGptPartitions (
487 IN PEI_FAT_PRIVATE_DATA *PrivateData,
488 IN UINTN ParentBlockDevNo
489 )
490{
491 BOOLEAN Found;
492 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
493
494 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
495 return FALSE;
496 }
497
498 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
499 if (ParentBlockDev->BlockSize > PEI_FAT_MAX_BLOCK_SIZE) {
500 DEBUG ((DEBUG_ERROR, "Device BlockSize %x exceed FAT_MAX_BLOCK_SIZE\n", ParentBlockDev->BlockSize));
501 return FALSE;
502 }
503
504 if (!PartitionCheckProtectiveMbr (PrivateData, ParentBlockDevNo)) {
505 return FALSE;
506 }
507
508 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, TRUE);
509 if (!Found) {
510 DEBUG ((DEBUG_ERROR, "Primary GPT Header Error, Try to Check Backup GPT Header!\n"));
511 Found = PartitionCheckGptStructure (PrivateData, ParentBlockDevNo, FALSE);
512 }
513
514 if (Found) {
515 ParentBlockDev->PartitionChecked = TRUE;
516 }
517
518 return Found;
519}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette