/src/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c
Line | Count | Source |
1 | | /** @file |
2 | | Decode a hard disk partitioned with the GPT scheme in the UEFI 2.0 |
3 | | specification. |
4 | | |
5 | | Caution: This file requires additional review when modified. |
6 | | This driver will have external input - disk partition. |
7 | | This external input must be validated carefully to avoid security issue like |
8 | | buffer overflow, integer overflow. |
9 | | |
10 | | PartitionInstallGptChildHandles() routine will read disk partition content and |
11 | | do basic validation before PartitionInstallChildHandle(). |
12 | | |
13 | | PartitionValidGptTable(), PartitionCheckGptEntry() routine will accept disk |
14 | | partition content and validate the GPT table and GPT entry. |
15 | | |
16 | | Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. |
17 | | Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> |
18 | | SPDX-License-Identifier: BSD-2-Clause-Patent |
19 | | |
20 | | **/ |
21 | | |
22 | | #include "Partition.h" |
23 | | |
24 | | /** |
25 | | Install child handles if the Handle supports GPT partition structure. |
26 | | |
27 | | Caution: This function may receive untrusted input. |
28 | | The GPT partition table header is external input, so this routine |
29 | | will do basic validation for GPT partition table header before return. |
30 | | |
31 | | @param[in] BlockIo Parent BlockIo interface. |
32 | | @param[in] DiskIo Disk Io protocol. |
33 | | @param[in] Lba The starting Lba of the Partition Table |
34 | | @param[out] PartHeader Stores the partition table that is read |
35 | | |
36 | | @retval TRUE The partition table is valid |
37 | | @retval FALSE The partition table is not valid |
38 | | |
39 | | **/ |
40 | | BOOLEAN |
41 | | PartitionValidGptTable ( |
42 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
43 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
44 | | IN EFI_LBA Lba, |
45 | | OUT EFI_PARTITION_TABLE_HEADER *PartHeader |
46 | | ); |
47 | | |
48 | | /** |
49 | | Check if the CRC field in the Partition table header is valid |
50 | | for Partition entry array. |
51 | | |
52 | | @param[in] BlockIo Parent BlockIo interface |
53 | | @param[in] DiskIo Disk Io Protocol. |
54 | | @param[in] PartHeader Partition table header structure |
55 | | |
56 | | @retval TRUE the CRC is valid |
57 | | @retval FALSE the CRC is invalid |
58 | | |
59 | | **/ |
60 | | BOOLEAN |
61 | | PartitionCheckGptEntryArrayCRC ( |
62 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
63 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
64 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader |
65 | | ); |
66 | | |
67 | | /** |
68 | | Restore Partition Table to its alternate place |
69 | | (Primary -> Backup or Backup -> Primary). |
70 | | |
71 | | @param[in] BlockIo Parent BlockIo interface. |
72 | | @param[in] DiskIo Disk Io Protocol. |
73 | | @param[in] PartHeader Partition table header structure. |
74 | | |
75 | | @retval TRUE Restoring succeeds |
76 | | @retval FALSE Restoring failed |
77 | | |
78 | | **/ |
79 | | BOOLEAN |
80 | | PartitionRestoreGptTable ( |
81 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
82 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
83 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader |
84 | | ); |
85 | | |
86 | | /** |
87 | | This routine will check GPT partition entry and return entry status. |
88 | | |
89 | | Caution: This function may receive untrusted input. |
90 | | The GPT partition entry is external input, so this routine |
91 | | will do basic validation for GPT partition entry and report status. |
92 | | |
93 | | @param[in] PartHeader Partition table header structure |
94 | | @param[in] PartEntry The partition entry array |
95 | | @param[out] PEntryStatus the partition entry status array |
96 | | recording the status of each partition |
97 | | |
98 | | **/ |
99 | | VOID |
100 | | PartitionCheckGptEntry ( |
101 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader, |
102 | | IN EFI_PARTITION_ENTRY *PartEntry, |
103 | | OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus |
104 | | ); |
105 | | |
106 | | /** |
107 | | Checks the CRC32 value in the table header. |
108 | | |
109 | | @param MaxSize Max Size limit |
110 | | @param Size The size of the table |
111 | | @param Hdr Table to check |
112 | | |
113 | | @return TRUE CRC Valid |
114 | | @return FALSE CRC Invalid |
115 | | |
116 | | **/ |
117 | | BOOLEAN |
118 | | PartitionCheckCrcAltSize ( |
119 | | IN UINTN MaxSize, |
120 | | IN UINTN Size, |
121 | | IN OUT EFI_TABLE_HEADER *Hdr |
122 | | ); |
123 | | |
124 | | /** |
125 | | Checks the CRC32 value in the table header. |
126 | | |
127 | | @param MaxSize Max Size limit |
128 | | @param Hdr Table to check |
129 | | |
130 | | @return TRUE CRC Valid |
131 | | @return FALSE CRC Invalid |
132 | | |
133 | | **/ |
134 | | BOOLEAN |
135 | | PartitionCheckCrc ( |
136 | | IN UINTN MaxSize, |
137 | | IN OUT EFI_TABLE_HEADER *Hdr |
138 | | ); |
139 | | |
140 | | /** |
141 | | Updates the CRC32 value in the table header. |
142 | | |
143 | | @param Size The size of the table |
144 | | @param Hdr Table to update |
145 | | |
146 | | **/ |
147 | | VOID |
148 | | PartitionSetCrcAltSize ( |
149 | | IN UINTN Size, |
150 | | IN OUT EFI_TABLE_HEADER *Hdr |
151 | | ); |
152 | | |
153 | | /** |
154 | | Updates the CRC32 value in the table header. |
155 | | |
156 | | @param Hdr Table to update |
157 | | |
158 | | **/ |
159 | | VOID |
160 | | PartitionSetCrc ( |
161 | | IN OUT EFI_TABLE_HEADER *Hdr |
162 | | ); |
163 | | |
164 | | /** |
165 | | Install child handles if the Handle supports GPT partition structure. |
166 | | |
167 | | Caution: This function may receive untrusted input. |
168 | | The GPT partition table is external input, so this routine |
169 | | will do basic validation for GPT partition table before install |
170 | | child handle for each GPT partition. |
171 | | |
172 | | @param[in] This Calling context. |
173 | | @param[in] Handle Parent Handle. |
174 | | @param[in] DiskIo Parent DiskIo interface. |
175 | | @param[in] DiskIo2 Parent DiskIo2 interface. |
176 | | @param[in] BlockIo Parent BlockIo interface. |
177 | | @param[in] BlockIo2 Parent BlockIo2 interface. |
178 | | @param[in] DevicePath Parent Device Path. |
179 | | |
180 | | @retval EFI_SUCCESS Valid GPT disk. |
181 | | @retval EFI_MEDIA_CHANGED Media changed Detected. |
182 | | @retval other Not a valid GPT disk. |
183 | | |
184 | | **/ |
185 | | EFI_STATUS |
186 | | PartitionInstallGptChildHandles ( |
187 | | IN EFI_DRIVER_BINDING_PROTOCOL *This, |
188 | | IN EFI_HANDLE Handle, |
189 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
190 | | IN EFI_DISK_IO2_PROTOCOL *DiskIo2, |
191 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
192 | | IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, |
193 | | IN EFI_DEVICE_PATH_PROTOCOL *DevicePath |
194 | | ) |
195 | 0 | { |
196 | 0 | EFI_STATUS Status; |
197 | 0 | UINT32 BlockSize; |
198 | 0 | EFI_LBA LastBlock; |
199 | 0 | MASTER_BOOT_RECORD *ProtectiveMbr; |
200 | 0 | EFI_PARTITION_TABLE_HEADER *PrimaryHeader; |
201 | 0 | EFI_PARTITION_TABLE_HEADER *BackupHeader; |
202 | 0 | EFI_PARTITION_ENTRY *PartEntry; |
203 | 0 | EFI_PARTITION_ENTRY *Entry; |
204 | 0 | EFI_PARTITION_ENTRY_STATUS *PEntryStatus; |
205 | 0 | UINTN Index; |
206 | 0 | EFI_STATUS GptValidStatus; |
207 | 0 | HARDDRIVE_DEVICE_PATH HdDev; |
208 | 0 | UINT32 MediaId; |
209 | 0 | EFI_PARTITION_INFO_PROTOCOL PartitionInfo; |
210 | |
|
211 | 0 | ProtectiveMbr = NULL; |
212 | 0 | PrimaryHeader = NULL; |
213 | 0 | BackupHeader = NULL; |
214 | 0 | PartEntry = NULL; |
215 | 0 | PEntryStatus = NULL; |
216 | |
|
217 | 0 | BlockSize = BlockIo->Media->BlockSize; |
218 | 0 | LastBlock = BlockIo->Media->LastBlock; |
219 | 0 | MediaId = BlockIo->Media->MediaId; |
220 | |
|
221 | 0 | DEBUG ((DEBUG_INFO, " BlockSize : %d \n", BlockSize)); |
222 | 0 | DEBUG ((DEBUG_INFO, " LastBlock : %lx \n", LastBlock)); |
223 | |
|
224 | 0 | GptValidStatus = EFI_NOT_FOUND; |
225 | | |
226 | | // |
227 | | // Ensure the block size can hold the MBR |
228 | | // |
229 | 0 | if (BlockSize < sizeof (MASTER_BOOT_RECORD)) { |
230 | 0 | return EFI_NOT_FOUND; |
231 | 0 | } |
232 | | |
233 | | // |
234 | | // Allocate a buffer for the Protective MBR |
235 | | // |
236 | 0 | ProtectiveMbr = AllocatePool (BlockSize); |
237 | 0 | if (ProtectiveMbr == NULL) { |
238 | 0 | return EFI_NOT_FOUND; |
239 | 0 | } |
240 | | |
241 | | // |
242 | | // Read the Protective MBR from LBA #0 |
243 | | // |
244 | 0 | Status = DiskIo->ReadDisk ( |
245 | 0 | DiskIo, |
246 | 0 | MediaId, |
247 | 0 | 0, |
248 | 0 | BlockSize, |
249 | 0 | ProtectiveMbr |
250 | 0 | ); |
251 | 0 | if (EFI_ERROR (Status)) { |
252 | 0 | GptValidStatus = Status; |
253 | 0 | goto Done; |
254 | 0 | } |
255 | | |
256 | | // |
257 | | // Verify that the Protective MBR is valid |
258 | | // |
259 | 0 | for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { |
260 | 0 | if ((ProtectiveMbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) && |
261 | 0 | (UNPACK_UINT32 (ProtectiveMbr->Partition[Index].StartingLBA) == 1) |
262 | 0 | ) |
263 | 0 | { |
264 | 0 | break; |
265 | 0 | } |
266 | 0 | } |
267 | |
|
268 | 0 | if (Index == MAX_MBR_PARTITIONS) { |
269 | 0 | goto Done; |
270 | 0 | } |
271 | | |
272 | | // |
273 | | // Allocate the GPT structures |
274 | | // |
275 | 0 | PrimaryHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); |
276 | 0 | if (PrimaryHeader == NULL) { |
277 | 0 | goto Done; |
278 | 0 | } |
279 | | |
280 | 0 | BackupHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); |
281 | 0 | if (BackupHeader == NULL) { |
282 | 0 | goto Done; |
283 | 0 | } |
284 | | |
285 | | // |
286 | | // Check primary and backup partition tables |
287 | | // |
288 | 0 | if (!PartitionValidGptTable (BlockIo, DiskIo, PRIMARY_PART_HEADER_LBA, PrimaryHeader)) { |
289 | 0 | DEBUG ((DEBUG_INFO, " Not Valid primary partition table\n")); |
290 | |
|
291 | 0 | if (!PartitionValidGptTable (BlockIo, DiskIo, LastBlock, BackupHeader)) { |
292 | 0 | DEBUG ((DEBUG_INFO, " Not Valid backup partition table\n")); |
293 | 0 | goto Done; |
294 | 0 | } else { |
295 | 0 | DEBUG ((DEBUG_INFO, " Valid backup partition table\n")); |
296 | 0 | DEBUG ((DEBUG_INFO, " Restore primary partition table by the backup\n")); |
297 | 0 | if (!PartitionRestoreGptTable (BlockIo, DiskIo, BackupHeader)) { |
298 | 0 | DEBUG ((DEBUG_INFO, " Restore primary partition table error\n")); |
299 | 0 | } |
300 | |
|
301 | 0 | if (PartitionValidGptTable (BlockIo, DiskIo, BackupHeader->AlternateLBA, PrimaryHeader)) { |
302 | 0 | DEBUG ((DEBUG_INFO, " Restore backup partition table success\n")); |
303 | 0 | } |
304 | 0 | } |
305 | 0 | } else if (!PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) { |
306 | 0 | DEBUG ((DEBUG_INFO, " Valid primary and !Valid backup partition table\n")); |
307 | 0 | DEBUG ((DEBUG_INFO, " Restore backup partition table by the primary\n")); |
308 | 0 | if (!PartitionRestoreGptTable (BlockIo, DiskIo, PrimaryHeader)) { |
309 | 0 | DEBUG ((DEBUG_INFO, " Restore backup partition table error\n")); |
310 | 0 | } |
311 | |
|
312 | 0 | if (PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) { |
313 | 0 | DEBUG ((DEBUG_INFO, " Restore backup partition table success\n")); |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | 0 | DEBUG ((DEBUG_INFO, " Valid primary and Valid backup partition table\n")); |
318 | | |
319 | | // |
320 | | // Read the EFI Partition Entries |
321 | | // |
322 | 0 | PartEntry = AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); |
323 | 0 | if (PartEntry == NULL) { |
324 | 0 | DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); |
325 | 0 | goto Done; |
326 | 0 | } |
327 | | |
328 | 0 | Status = DiskIo->ReadDisk ( |
329 | 0 | DiskIo, |
330 | 0 | MediaId, |
331 | 0 | MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockSize), |
332 | 0 | PrimaryHeader->NumberOfPartitionEntries * (PrimaryHeader->SizeOfPartitionEntry), |
333 | 0 | PartEntry |
334 | 0 | ); |
335 | 0 | if (EFI_ERROR (Status)) { |
336 | 0 | GptValidStatus = Status; |
337 | 0 | DEBUG ((DEBUG_ERROR, " Partition Entry ReadDisk error\n")); |
338 | 0 | goto Done; |
339 | 0 | } |
340 | | |
341 | 0 | DEBUG ((DEBUG_INFO, " Partition entries read block success\n")); |
342 | |
|
343 | 0 | DEBUG ((DEBUG_INFO, " Number of partition entries: %d\n", PrimaryHeader->NumberOfPartitionEntries)); |
344 | |
|
345 | 0 | PEntryStatus = AllocateZeroPool (PrimaryHeader->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)); |
346 | 0 | if (PEntryStatus == NULL) { |
347 | 0 | DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); |
348 | 0 | goto Done; |
349 | 0 | } |
350 | | |
351 | | // |
352 | | // Check the integrity of partition entries |
353 | | // |
354 | 0 | PartitionCheckGptEntry (PrimaryHeader, PartEntry, PEntryStatus); |
355 | | |
356 | | // |
357 | | // If we got this far the GPT layout of the disk is valid and we should return true |
358 | | // |
359 | 0 | GptValidStatus = EFI_SUCCESS; |
360 | | |
361 | | // |
362 | | // Create child device handles |
363 | | // |
364 | 0 | for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { |
365 | 0 | Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index * PrimaryHeader->SizeOfPartitionEntry); |
366 | 0 | if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid) || |
367 | 0 | PEntryStatus[Index].OutOfRange || |
368 | 0 | PEntryStatus[Index].Overlap || |
369 | 0 | PEntryStatus[Index].OsSpecific |
370 | 0 | ) |
371 | 0 | { |
372 | | // |
373 | | // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific |
374 | | // partition Entries |
375 | | // |
376 | 0 | continue; |
377 | 0 | } |
378 | | |
379 | 0 | ZeroMem (&HdDev, sizeof (HdDev)); |
380 | 0 | HdDev.Header.Type = MEDIA_DEVICE_PATH; |
381 | 0 | HdDev.Header.SubType = MEDIA_HARDDRIVE_DP; |
382 | 0 | SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev)); |
383 | |
|
384 | 0 | HdDev.PartitionNumber = (UINT32)Index + 1; |
385 | 0 | HdDev.MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; |
386 | 0 | HdDev.SignatureType = SIGNATURE_TYPE_GUID; |
387 | 0 | HdDev.PartitionStart = Entry->StartingLBA; |
388 | 0 | HdDev.PartitionSize = Entry->EndingLBA - Entry->StartingLBA + 1; |
389 | 0 | CopyMem (HdDev.Signature, &Entry->UniquePartitionGUID, sizeof (EFI_GUID)); |
390 | |
|
391 | 0 | ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); |
392 | 0 | PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; |
393 | 0 | PartitionInfo.Type = PARTITION_TYPE_GPT; |
394 | 0 | if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeSystemPartGuid)) { |
395 | 0 | PartitionInfo.System = 1; |
396 | 0 | } |
397 | |
|
398 | 0 | CopyMem (&PartitionInfo.Info.Gpt, Entry, sizeof (EFI_PARTITION_ENTRY)); |
399 | |
|
400 | 0 | DEBUG ((DEBUG_INFO, " Index : %d\n", (UINT32)Index)); |
401 | 0 | DEBUG ((DEBUG_INFO, " Start LBA : %lx\n", (UINT64)HdDev.PartitionStart)); |
402 | 0 | DEBUG ((DEBUG_INFO, " End LBA : %lx\n", (UINT64)Entry->EndingLBA)); |
403 | 0 | DEBUG ((DEBUG_INFO, " Partition size: %lx\n", (UINT64)HdDev.PartitionSize)); |
404 | 0 | DEBUG ((DEBUG_INFO, " Start : %lx", MultU64x32 (Entry->StartingLBA, BlockSize))); |
405 | 0 | DEBUG ((DEBUG_INFO, " End : %lx\n", MultU64x32 (Entry->EndingLBA, BlockSize))); |
406 | |
|
407 | 0 | Status = PartitionInstallChildHandle ( |
408 | 0 | This, |
409 | 0 | Handle, |
410 | 0 | DiskIo, |
411 | 0 | DiskIo2, |
412 | 0 | BlockIo, |
413 | 0 | BlockIo2, |
414 | 0 | DevicePath, |
415 | 0 | (EFI_DEVICE_PATH_PROTOCOL *)&HdDev, |
416 | 0 | &PartitionInfo, |
417 | 0 | Entry->StartingLBA, |
418 | 0 | Entry->EndingLBA, |
419 | 0 | BlockSize, |
420 | 0 | &Entry->PartitionTypeGUID |
421 | 0 | ); |
422 | 0 | } |
423 | |
|
424 | 0 | DEBUG ((DEBUG_INFO, "Prepare to Free Pool\n")); |
425 | |
|
426 | 0 | Done: |
427 | 0 | if (ProtectiveMbr != NULL) { |
428 | 0 | FreePool (ProtectiveMbr); |
429 | 0 | } |
430 | |
|
431 | 0 | if (PrimaryHeader != NULL) { |
432 | 0 | FreePool (PrimaryHeader); |
433 | 0 | } |
434 | |
|
435 | 0 | if (BackupHeader != NULL) { |
436 | 0 | FreePool (BackupHeader); |
437 | 0 | } |
438 | |
|
439 | 0 | if (PartEntry != NULL) { |
440 | 0 | FreePool (PartEntry); |
441 | 0 | } |
442 | |
|
443 | 0 | if (PEntryStatus != NULL) { |
444 | 0 | FreePool (PEntryStatus); |
445 | 0 | } |
446 | |
|
447 | 0 | return GptValidStatus; |
448 | 0 | } |
449 | | |
450 | | /** |
451 | | This routine will read GPT partition table header and return it. |
452 | | |
453 | | Caution: This function may receive untrusted input. |
454 | | The GPT partition table header is external input, so this routine |
455 | | will do basic validation for GPT partition table header before return. |
456 | | |
457 | | @param[in] BlockIo Parent BlockIo interface. |
458 | | @param[in] DiskIo Disk Io protocol. |
459 | | @param[in] Lba The starting Lba of the Partition Table |
460 | | @param[out] PartHeader Stores the partition table that is read |
461 | | |
462 | | @retval TRUE The partition table is valid |
463 | | @retval FALSE The partition table is not valid |
464 | | |
465 | | **/ |
466 | | BOOLEAN |
467 | | PartitionValidGptTable ( |
468 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
469 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
470 | | IN EFI_LBA Lba, |
471 | | OUT EFI_PARTITION_TABLE_HEADER *PartHeader |
472 | | ) |
473 | 0 | { |
474 | 0 | EFI_STATUS Status; |
475 | 0 | UINT32 BlockSize; |
476 | 0 | EFI_PARTITION_TABLE_HEADER *PartHdr; |
477 | 0 | UINT32 MediaId; |
478 | |
|
479 | 0 | BlockSize = BlockIo->Media->BlockSize; |
480 | 0 | MediaId = BlockIo->Media->MediaId; |
481 | 0 | PartHdr = AllocateZeroPool (BlockSize); |
482 | |
|
483 | 0 | if (PartHdr == NULL) { |
484 | 0 | DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); |
485 | 0 | return FALSE; |
486 | 0 | } |
487 | | |
488 | | // |
489 | | // Read the EFI Partition Table Header |
490 | | // |
491 | 0 | Status = DiskIo->ReadDisk ( |
492 | 0 | DiskIo, |
493 | 0 | MediaId, |
494 | 0 | MultU64x32 (Lba, BlockSize), |
495 | 0 | BlockSize, |
496 | 0 | PartHdr |
497 | 0 | ); |
498 | 0 | if (EFI_ERROR (Status)) { |
499 | 0 | FreePool (PartHdr); |
500 | 0 | return FALSE; |
501 | 0 | } |
502 | | |
503 | 0 | if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || |
504 | 0 | !PartitionCheckCrc (BlockSize, &PartHdr->Header) || |
505 | 0 | (PartHdr->MyLBA != Lba) || |
506 | 0 | (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY)) |
507 | 0 | ) |
508 | 0 | { |
509 | 0 | DEBUG ((DEBUG_INFO, "Invalid efi partition table header\n")); |
510 | 0 | FreePool (PartHdr); |
511 | 0 | return FALSE; |
512 | 0 | } |
513 | | |
514 | | // |
515 | | // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. |
516 | | // |
517 | 0 | if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { |
518 | 0 | FreePool (PartHdr); |
519 | 0 | return FALSE; |
520 | 0 | } |
521 | | |
522 | 0 | CopyMem (PartHeader, PartHdr, sizeof (EFI_PARTITION_TABLE_HEADER)); |
523 | 0 | if (!PartitionCheckGptEntryArrayCRC (BlockIo, DiskIo, PartHeader)) { |
524 | 0 | FreePool (PartHdr); |
525 | 0 | return FALSE; |
526 | 0 | } |
527 | | |
528 | 0 | DEBUG ((DEBUG_INFO, " Valid efi partition table header\n")); |
529 | 0 | FreePool (PartHdr); |
530 | 0 | return TRUE; |
531 | 0 | } |
532 | | |
533 | | /** |
534 | | Check if the CRC field in the Partition table header is valid |
535 | | for Partition entry array. |
536 | | |
537 | | @param[in] BlockIo Parent BlockIo interface |
538 | | @param[in] DiskIo Disk Io Protocol. |
539 | | @param[in] PartHeader Partition table header structure |
540 | | |
541 | | @retval TRUE the CRC is valid |
542 | | @retval FALSE the CRC is invalid |
543 | | |
544 | | **/ |
545 | | BOOLEAN |
546 | | PartitionCheckGptEntryArrayCRC ( |
547 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
548 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
549 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader |
550 | | ) |
551 | 0 | { |
552 | 0 | EFI_STATUS Status; |
553 | 0 | UINT8 *Ptr; |
554 | 0 | UINT32 Crc; |
555 | 0 | UINTN Size; |
556 | | |
557 | | // |
558 | | // Read the EFI Partition Entries |
559 | | // |
560 | 0 | Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); |
561 | 0 | if (Ptr == NULL) { |
562 | 0 | DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); |
563 | 0 | return FALSE; |
564 | 0 | } |
565 | | |
566 | 0 | Status = DiskIo->ReadDisk ( |
567 | 0 | DiskIo, |
568 | 0 | BlockIo->Media->MediaId, |
569 | 0 | MultU64x32 (PartHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), |
570 | 0 | PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, |
571 | 0 | Ptr |
572 | 0 | ); |
573 | 0 | if (EFI_ERROR (Status)) { |
574 | 0 | FreePool (Ptr); |
575 | 0 | return FALSE; |
576 | 0 | } |
577 | | |
578 | 0 | Size = PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry; |
579 | |
|
580 | 0 | Status = gBS->CalculateCrc32 (Ptr, Size, &Crc); |
581 | 0 | if (EFI_ERROR (Status)) { |
582 | 0 | DEBUG ((DEBUG_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n")); |
583 | 0 | FreePool (Ptr); |
584 | 0 | return FALSE; |
585 | 0 | } |
586 | | |
587 | 0 | FreePool (Ptr); |
588 | |
|
589 | 0 | return (BOOLEAN)(PartHeader->PartitionEntryArrayCRC32 == Crc); |
590 | 0 | } |
591 | | |
592 | | /** |
593 | | Restore Partition Table to its alternate place |
594 | | (Primary -> Backup or Backup -> Primary). |
595 | | |
596 | | @param[in] BlockIo Parent BlockIo interface. |
597 | | @param[in] DiskIo Disk Io Protocol. |
598 | | @param[in] PartHeader Partition table header structure. |
599 | | |
600 | | @retval TRUE Restoring succeeds |
601 | | @retval FALSE Restoring failed |
602 | | |
603 | | **/ |
604 | | BOOLEAN |
605 | | PartitionRestoreGptTable ( |
606 | | IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
607 | | IN EFI_DISK_IO_PROTOCOL *DiskIo, |
608 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader |
609 | | ) |
610 | 0 | { |
611 | 0 | EFI_STATUS Status; |
612 | 0 | UINTN BlockSize; |
613 | 0 | EFI_PARTITION_TABLE_HEADER *PartHdr; |
614 | 0 | EFI_LBA PEntryLBA; |
615 | 0 | UINT8 *Ptr; |
616 | 0 | UINT32 MediaId; |
617 | |
|
618 | 0 | PartHdr = NULL; |
619 | 0 | Ptr = NULL; |
620 | |
|
621 | 0 | BlockSize = BlockIo->Media->BlockSize; |
622 | 0 | MediaId = BlockIo->Media->MediaId; |
623 | |
|
624 | 0 | PartHdr = AllocateZeroPool (BlockSize); |
625 | |
|
626 | 0 | if (PartHdr == NULL) { |
627 | 0 | DEBUG ((DEBUG_ERROR, "Allocate pool error\n")); |
628 | 0 | return FALSE; |
629 | 0 | } |
630 | | |
631 | 0 | PEntryLBA = (PartHeader->MyLBA == PRIMARY_PART_HEADER_LBA) ? \ |
632 | 0 | (PartHeader->LastUsableLBA + 1) : \ |
633 | 0 | (PRIMARY_PART_HEADER_LBA + 1); |
634 | |
|
635 | 0 | CopyMem (PartHdr, PartHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); |
636 | |
|
637 | 0 | PartHdr->MyLBA = PartHeader->AlternateLBA; |
638 | 0 | PartHdr->AlternateLBA = PartHeader->MyLBA; |
639 | 0 | PartHdr->PartitionEntryLBA = PEntryLBA; |
640 | 0 | PartitionSetCrc ((EFI_TABLE_HEADER *)PartHdr); |
641 | |
|
642 | 0 | Status = DiskIo->WriteDisk ( |
643 | 0 | DiskIo, |
644 | 0 | MediaId, |
645 | 0 | MultU64x32 (PartHdr->MyLBA, (UINT32)BlockSize), |
646 | 0 | BlockSize, |
647 | 0 | PartHdr |
648 | 0 | ); |
649 | 0 | if (EFI_ERROR (Status)) { |
650 | 0 | goto Done; |
651 | 0 | } |
652 | | |
653 | 0 | Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); |
654 | 0 | if (Ptr == NULL) { |
655 | 0 | DEBUG ((DEBUG_ERROR, " Allocate pool error\n")); |
656 | 0 | Status = EFI_OUT_OF_RESOURCES; |
657 | 0 | goto Done; |
658 | 0 | } |
659 | | |
660 | 0 | Status = DiskIo->ReadDisk ( |
661 | 0 | DiskIo, |
662 | 0 | MediaId, |
663 | 0 | MultU64x32 (PartHeader->PartitionEntryLBA, (UINT32)BlockSize), |
664 | 0 | PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, |
665 | 0 | Ptr |
666 | 0 | ); |
667 | 0 | if (EFI_ERROR (Status)) { |
668 | 0 | goto Done; |
669 | 0 | } |
670 | | |
671 | 0 | Status = DiskIo->WriteDisk ( |
672 | 0 | DiskIo, |
673 | 0 | MediaId, |
674 | 0 | MultU64x32 (PEntryLBA, (UINT32)BlockSize), |
675 | 0 | PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, |
676 | 0 | Ptr |
677 | 0 | ); |
678 | |
|
679 | 0 | Done: |
680 | 0 | FreePool (PartHdr); |
681 | |
|
682 | 0 | if (Ptr != NULL) { |
683 | 0 | FreePool (Ptr); |
684 | 0 | } |
685 | |
|
686 | 0 | if (EFI_ERROR (Status)) { |
687 | 0 | return FALSE; |
688 | 0 | } |
689 | | |
690 | 0 | return TRUE; |
691 | 0 | } |
692 | | |
693 | | /** |
694 | | This routine will check GPT partition entry and return entry status. |
695 | | |
696 | | Caution: This function may receive untrusted input. |
697 | | The GPT partition entry is external input, so this routine |
698 | | will do basic validation for GPT partition entry and report status. |
699 | | |
700 | | @param[in] PartHeader Partition table header structure |
701 | | @param[in] PartEntry The partition entry array |
702 | | @param[out] PEntryStatus the partition entry status array |
703 | | recording the status of each partition |
704 | | |
705 | | **/ |
706 | | VOID |
707 | | PartitionCheckGptEntry ( |
708 | | IN EFI_PARTITION_TABLE_HEADER *PartHeader, |
709 | | IN EFI_PARTITION_ENTRY *PartEntry, |
710 | | OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus |
711 | | ) |
712 | 0 | { |
713 | 0 | EFI_LBA StartingLBA; |
714 | 0 | EFI_LBA EndingLBA; |
715 | 0 | EFI_PARTITION_ENTRY *Entry; |
716 | 0 | UINTN Index1; |
717 | 0 | UINTN Index2; |
718 | |
|
719 | 0 | DEBUG ((DEBUG_INFO, " start check partition entries\n")); |
720 | 0 | for (Index1 = 0; Index1 < PartHeader->NumberOfPartitionEntries; Index1++) { |
721 | 0 | Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index1 * PartHeader->SizeOfPartitionEntry); |
722 | 0 | if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { |
723 | 0 | continue; |
724 | 0 | } |
725 | | |
726 | 0 | StartingLBA = Entry->StartingLBA; |
727 | 0 | EndingLBA = Entry->EndingLBA; |
728 | 0 | if ((StartingLBA > EndingLBA) || |
729 | 0 | (StartingLBA < PartHeader->FirstUsableLBA) || |
730 | 0 | (StartingLBA > PartHeader->LastUsableLBA) || |
731 | 0 | (EndingLBA < PartHeader->FirstUsableLBA) || |
732 | 0 | (EndingLBA > PartHeader->LastUsableLBA) |
733 | 0 | ) |
734 | 0 | { |
735 | 0 | PEntryStatus[Index1].OutOfRange = TRUE; |
736 | 0 | continue; |
737 | 0 | } |
738 | | |
739 | 0 | if ((Entry->Attributes & BIT1) != 0) { |
740 | | // |
741 | | // If Bit 1 is set, this indicate that this is an OS specific GUID partition. |
742 | | // |
743 | 0 | PEntryStatus[Index1].OsSpecific = TRUE; |
744 | 0 | } |
745 | |
|
746 | 0 | for (Index2 = Index1 + 1; Index2 < PartHeader->NumberOfPartitionEntries; Index2++) { |
747 | 0 | Entry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartEntry + Index2 * PartHeader->SizeOfPartitionEntry); |
748 | 0 | if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { |
749 | 0 | continue; |
750 | 0 | } |
751 | | |
752 | 0 | if ((Entry->EndingLBA >= StartingLBA) && (Entry->StartingLBA <= EndingLBA)) { |
753 | | // |
754 | | // This region overlaps with the Index1'th region |
755 | | // |
756 | 0 | PEntryStatus[Index1].Overlap = TRUE; |
757 | 0 | PEntryStatus[Index2].Overlap = TRUE; |
758 | 0 | continue; |
759 | 0 | } |
760 | 0 | } |
761 | 0 | } |
762 | |
|
763 | 0 | DEBUG ((DEBUG_INFO, " End check partition entries\n")); |
764 | 0 | } |
765 | | |
766 | | /** |
767 | | Updates the CRC32 value in the table header. |
768 | | |
769 | | @param Hdr Table to update |
770 | | |
771 | | **/ |
772 | | VOID |
773 | | PartitionSetCrc ( |
774 | | IN OUT EFI_TABLE_HEADER *Hdr |
775 | | ) |
776 | 0 | { |
777 | 0 | PartitionSetCrcAltSize (Hdr->HeaderSize, Hdr); |
778 | 0 | } |
779 | | |
780 | | /** |
781 | | Updates the CRC32 value in the table header. |
782 | | |
783 | | @param Size The size of the table |
784 | | @param Hdr Table to update |
785 | | |
786 | | **/ |
787 | | VOID |
788 | | PartitionSetCrcAltSize ( |
789 | | IN UINTN Size, |
790 | | IN OUT EFI_TABLE_HEADER *Hdr |
791 | | ) |
792 | 0 | { |
793 | 0 | UINT32 Crc; |
794 | |
|
795 | 0 | Hdr->CRC32 = 0; |
796 | 0 | gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); |
797 | 0 | Hdr->CRC32 = Crc; |
798 | 0 | } |
799 | | |
800 | | /** |
801 | | Checks the CRC32 value in the table header. |
802 | | |
803 | | @param MaxSize Max Size limit |
804 | | @param Hdr Table to check |
805 | | |
806 | | @return TRUE CRC Valid |
807 | | @return FALSE CRC Invalid |
808 | | |
809 | | **/ |
810 | | BOOLEAN |
811 | | PartitionCheckCrc ( |
812 | | IN UINTN MaxSize, |
813 | | IN OUT EFI_TABLE_HEADER *Hdr |
814 | | ) |
815 | 0 | { |
816 | 0 | return PartitionCheckCrcAltSize (MaxSize, Hdr->HeaderSize, Hdr); |
817 | 0 | } |
818 | | |
819 | | /** |
820 | | Checks the CRC32 value in the table header. |
821 | | |
822 | | @param MaxSize Max Size limit |
823 | | @param Size The size of the table |
824 | | @param Hdr Table to check |
825 | | |
826 | | @return TRUE CRC Valid |
827 | | @return FALSE CRC Invalid |
828 | | |
829 | | **/ |
830 | | BOOLEAN |
831 | | PartitionCheckCrcAltSize ( |
832 | | IN UINTN MaxSize, |
833 | | IN UINTN Size, |
834 | | IN OUT EFI_TABLE_HEADER *Hdr |
835 | | ) |
836 | 0 | { |
837 | 0 | UINT32 Crc; |
838 | 0 | UINT32 OrgCrc; |
839 | 0 | EFI_STATUS Status; |
840 | |
|
841 | 0 | Crc = 0; |
842 | |
|
843 | 0 | if (Size == 0) { |
844 | | // |
845 | | // If header size is 0 CRC will pass so return FALSE here |
846 | | // |
847 | 0 | return FALSE; |
848 | 0 | } |
849 | | |
850 | 0 | if ((MaxSize != 0) && (Size > MaxSize)) { |
851 | 0 | DEBUG ((DEBUG_ERROR, "CheckCrc32: Size > MaxSize\n")); |
852 | 0 | return FALSE; |
853 | 0 | } |
854 | | |
855 | | // |
856 | | // clear old crc from header |
857 | | // |
858 | 0 | OrgCrc = Hdr->CRC32; |
859 | 0 | Hdr->CRC32 = 0; |
860 | |
|
861 | 0 | Status = gBS->CalculateCrc32 ((UINT8 *)Hdr, Size, &Crc); |
862 | 0 | if (EFI_ERROR (Status)) { |
863 | 0 | DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc calculation failed\n")); |
864 | 0 | return FALSE; |
865 | 0 | } |
866 | | |
867 | | // |
868 | | // set results |
869 | | // |
870 | 0 | Hdr->CRC32 = Crc; |
871 | | |
872 | | // |
873 | | // return status |
874 | | // |
875 | 0 | DEBUG_CODE_BEGIN (); |
876 | 0 | if (OrgCrc != Crc) { |
877 | 0 | DEBUG ((DEBUG_ERROR, "CheckCrc32: Crc check failed\n")); |
878 | 0 | } |
879 | |
|
880 | 0 | DEBUG_CODE_END (); |
881 | |
|
882 | 0 | return (BOOLEAN)(OrgCrc == Crc); |
883 | 0 | } |