--- /dev/null
+/*++\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation \r
+All rights reserved. This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution. The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php \r
+ \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+Module Name:\r
+\r
+ Mbr.c\r
+ \r
+Abstract:\r
+\r
+ Decode a hard disk partitioned with the legacy MBR found on most PC's\r
+\r
+ MBR - Master Boot Record is in the first sector of a partitioned hard disk.\r
+ The MBR supports four partitions per disk. The MBR also contains legacy\r
+ code that is not run on an EFI system. The legacy code reads the \r
+ first sector of the active partition into memory and \r
+\r
+ BPB - Boot(?) Parameter Block is in the first sector of a FAT file system. \r
+ The BPB contains information about the FAT file system. The BPB is \r
+ always on the first sector of a media. The first sector also contains\r
+ the legacy boot strap code.\r
+\r
+--*/\r
+\r
+//\r
+// Include common header file for this module.\r
+//\r
+#include "CommonHeader.h"\r
+\r
+#include "Partition.h"\r
+\r
+STATIC\r
+BOOLEAN\r
+PartitionValidMbr (\r
+ IN MASTER_BOOT_RECORD *Mbr,\r
+ IN EFI_LBA LastLba\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Test to see if the Mbr buffer is a valid MBR\r
+\r
+Arguments: \r
+ Mbr - Parent Handle \r
+ LastLba - Last Lba address on the device.\r
+\r
+Returns:\r
+ TRUE - Mbr is a Valid MBR\r
+ FALSE - Mbr is not a Valid MBR\r
+\r
+--*/\r
+{\r
+ UINT32 StartingLBA;\r
+ UINT32 EndingLBA;\r
+ UINT32 NewEndingLBA;\r
+ INTN Index1;\r
+ INTN Index2;\r
+ BOOLEAN MbrValid;\r
+\r
+ if (Mbr->Signature != MBR_SIGNATURE) {\r
+ return FALSE;\r
+ }\r
+ //\r
+ // The BPB also has this signature, so it can not be used alone.\r
+ //\r
+ MbrValid = FALSE;\r
+ for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) {\r
+ if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) {\r
+ continue;\r
+ }\r
+\r
+ MbrValid = TRUE;\r
+ StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA);\r
+ EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1;\r
+ if (EndingLBA > LastLba) {\r
+ //\r
+ // Compatibility Errata:\r
+ // Some systems try to hide drive space with their INT 13h driver\r
+ // This does not hide space from the OS driver. This means the MBR\r
+ // that gets created from DOS is smaller than the MBR created from\r
+ // a real OS (NT & Win98). This leads to BlockIo->LastBlock being\r
+ // wrong on some systems FDISKed by the OS.\r
+ //\r
+ // return FALSE since no block devices on a system are implemented\r
+ // with INT 13h\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) {\r
+ if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) == 0) {\r
+ continue;\r
+ }\r
+\r
+ NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1;\r
+ if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) {\r
+ //\r
+ // This region overlaps with the Index1'th region\r
+ //\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Non of the regions overlapped so MBR is O.K.\r
+ //\r
+ return MbrValid;\r
+}\r
+\r
+EFI_STATUS\r
+PartitionInstallMbrChildHandles (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,\r
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+ Install child handles if the Handle supports MBR format.\r
+\r
+Arguments: \r
+ This - Calling context.\r
+ Handle - Parent Handle \r
+ DiskIo - Parent DiskIo interface\r
+ BlockIo - Parent BlockIo interface\r
+ DevicePath - Parent Device Path\r
+\r
+Returns:\r
+ EFI_SUCCESS - If a child handle was added\r
+ EFI_MEDIA_CHANGED - Media changed Detected\r
+ !EFI_SUCCESS - Not found MBR partition.\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ MASTER_BOOT_RECORD *Mbr;\r
+ UINT32 ExtMbrStartingLba;\r
+ UINTN Index;\r
+ HARDDRIVE_DEVICE_PATH HdDev;\r
+ HARDDRIVE_DEVICE_PATH ParentHdDev;\r
+ EFI_STATUS Found;\r
+ UINT32 PartitionNumber;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *LastDevicePathNode;\r
+\r
+ Mbr = NULL;\r
+ Found = EFI_NOT_FOUND;\r
+\r
+ Mbr = AllocatePool (BlockIo->Media->BlockSize);\r
+ if (Mbr == NULL) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = BlockIo->ReadBlocks (\r
+ BlockIo,\r
+ BlockIo->Media->MediaId,\r
+ 0,\r
+ BlockIo->Media->BlockSize,\r
+ Mbr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Found = Status;\r
+ goto Done;\r
+ }\r
+ if (!PartitionValidMbr (Mbr, BlockIo->Media->LastBlock)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // We have a valid mbr - add each partition\r
+ //\r
+ //\r
+ // Get starting and ending LBA of the parent block device.\r
+ //\r
+ LastDevicePathNode = NULL;\r
+ ZeroMem (&ParentHdDev, sizeof (ParentHdDev));\r
+ DevicePathNode = DevicePath;\r
+ while (!EfiIsDevicePathEnd (DevicePathNode)) {\r
+ LastDevicePathNode = DevicePathNode;\r
+ DevicePathNode = EfiNextDevicePathNode (DevicePathNode);\r
+ }\r
+\r
+ if (LastDevicePathNode != NULL) {\r
+ if (DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH &&\r
+ DevicePathSubType (LastDevicePathNode) == MEDIA_HARDDRIVE_DP\r
+ ) {\r
+ CopyMem (&ParentHdDev, LastDevicePathNode, sizeof (ParentHdDev));\r
+ } else {\r
+ LastDevicePathNode = NULL;\r
+ }\r
+ }\r
+\r
+ PartitionNumber = 1;\r
+\r
+ ZeroMem (&HdDev, sizeof (HdDev));\r
+ HdDev.Header.Type = MEDIA_DEVICE_PATH;\r
+ HdDev.Header.SubType = MEDIA_HARDDRIVE_DP;\r
+ SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev));\r
+ HdDev.MBRType = MBR_TYPE_PCAT;\r
+ HdDev.SignatureType = SIGNATURE_TYPE_MBR;\r
+\r
+ if (LastDevicePathNode == NULL) {\r
+ //\r
+ // This is a MBR, add each partition\r
+ //\r
+ for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {\r
+ if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA) == 0) {\r
+ //\r
+ // Don't use null MBR entries\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (Mbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) {\r
+ //\r
+ // This is the guard MBR for the GPT. If you ever see a GPT disk with zero partitions you can get here.\r
+ // We can not produce an MBR BlockIo for this device as the MBR spans the GPT headers. So formating \r
+ // this BlockIo would corrupt the GPT structures and require a recovery that would corrupt the format\r
+ // that corrupted the GPT partition. \r
+ //\r
+ continue;\r
+ }\r
+\r
+ HdDev.PartitionNumber = PartitionNumber ++;\r
+ HdDev.PartitionStart = UNPACK_UINT32 (Mbr->Partition[Index].StartingLBA);\r
+ HdDev.PartitionSize = UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA);\r
+ CopyMem (HdDev.Signature, &(Mbr->UniqueMbrSignature[0]), sizeof (UINT32));\r
+\r
+ Status = PartitionInstallChildHandle (\r
+ This,\r
+ Handle,\r
+ DiskIo,\r
+ BlockIo,\r
+ DevicePath,\r
+ (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,\r
+ HdDev.PartitionStart,\r
+ HdDev.PartitionStart + HdDev.PartitionSize - 1,\r
+ MBR_SIZE,\r
+ (BOOLEAN) (Mbr->Partition[Index].OSIndicator == EFI_PARTITION)\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Found = EFI_SUCCESS;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // It's an extended partition. Follow the extended partition\r
+ // chain to get all the logical drives\r
+ //\r
+ ExtMbrStartingLba = 0;\r
+\r
+ do {\r
+\r
+ Status = BlockIo->ReadBlocks (\r
+ BlockIo,\r
+ BlockIo->Media->MediaId,\r
+ ExtMbrStartingLba,\r
+ BlockIo->Media->BlockSize,\r
+ Mbr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Found = Status;\r
+ goto Done;\r
+ }\r
+\r
+ if (Mbr->Partition[0].OSIndicator == 0) {\r
+ break;\r
+ }\r
+\r
+ if ((Mbr->Partition[0].OSIndicator == EXTENDED_DOS_PARTITION) ||\r
+ (Mbr->Partition[0].OSIndicator == EXTENDED_WINDOWS_PARTITION)) {\r
+ ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA);\r
+ continue;\r
+ }\r
+ HdDev.PartitionNumber = PartitionNumber ++;\r
+ HdDev.PartitionStart = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA) + ExtMbrStartingLba + ParentHdDev.PartitionStart;\r
+ HdDev.PartitionSize = UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA);\r
+ if ((HdDev.PartitionStart + HdDev.PartitionSize - 1 >= ParentHdDev.PartitionStart + ParentHdDev.PartitionSize) ||\r
+ (HdDev.PartitionStart <= ParentHdDev.PartitionStart)) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // The signature in EBR(Extended Boot Record) should always be 0.\r
+ //\r
+ *((UINT32 *) &HdDev.Signature[0]) = 0;\r
+\r
+ Status = PartitionInstallChildHandle (\r
+ This,\r
+ Handle,\r
+ DiskIo,\r
+ BlockIo,\r
+ DevicePath,\r
+ (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,\r
+ HdDev.PartitionStart - ParentHdDev.PartitionStart,\r
+ HdDev.PartitionStart - ParentHdDev.PartitionStart + HdDev.PartitionSize - 1,\r
+ MBR_SIZE,\r
+ (BOOLEAN) (Mbr->Partition[0].OSIndicator == EFI_PARTITION)\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Found = EFI_SUCCESS;\r
+ }\r
+\r
+ if ((Mbr->Partition[1].OSIndicator != EXTENDED_DOS_PARTITION) &&\r
+ (Mbr->Partition[1].OSIndicator != EXTENDED_WINDOWS_PARTITION)\r
+ ) {\r
+ break;\r
+ }\r
+\r
+ ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[1].StartingLBA);\r
+ //\r
+ // Don't allow partition to be self referencing\r
+ //\r
+ if (ExtMbrStartingLba == 0) {\r
+ break;\r
+ }\r
+ } while (ExtMbrStartingLba < ParentHdDev.PartitionSize);\r
+ }\r
+\r
+Done:\r
+ FreePool (Mbr);\r
+\r
+ return Found;\r
+}\r