Home | History | Annotate | Download | only in PartitionDxe
      1 /** @file
      2   Decode a hard disk partitioned with the legacy MBR found on most PC's
      3 
      4   MBR - Master Boot Record is in the first sector of a partitioned hard disk.
      5         The MBR supports four partitions per disk. The MBR also contains legacy
      6         code that is not run on an EFI system. The legacy code reads the
      7         first sector of the active partition into memory and
      8 
      9   BPB - BIOS Parameter Block is in the first sector of a FAT file system.
     10         The BPB contains information about the FAT file system. The BPB is
     11         always on the first sector of a media. The first sector also contains
     12         the legacy boot strap code.
     13 
     14 Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
     15 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
     16 This program and the accompanying materials
     17 are licensed and made available under the terms and conditions of the BSD License
     18 which accompanies this distribution.  The full text of the license may be found at
     19 http://opensource.org/licenses/bsd-license.php
     20 
     21 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     22 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     23 
     24 **/
     25 
     26 #include "Partition.h"
     27 
     28 /**
     29   Test to see if the Mbr buffer is a valid MBR.
     30 
     31   @param  Mbr         Parent Handle.
     32   @param  LastLba     Last Lba address on the device.
     33 
     34   @retval TRUE        Mbr is a Valid MBR.
     35   @retval FALSE       Mbr is not a Valid MBR.
     36 
     37 **/
     38 BOOLEAN
     39 PartitionValidMbr (
     40   IN  MASTER_BOOT_RECORD      *Mbr,
     41   IN  EFI_LBA                 LastLba
     42   )
     43 {
     44   UINT32  StartingLBA;
     45   UINT32  EndingLBA;
     46   UINT32  NewEndingLBA;
     47   INTN    Index1;
     48   INTN    Index2;
     49   BOOLEAN MbrValid;
     50 
     51   if (Mbr->Signature != MBR_SIGNATURE) {
     52     return FALSE;
     53   }
     54   //
     55   // The BPB also has this signature, so it can not be used alone.
     56   //
     57   MbrValid = FALSE;
     58   for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) {
     59     if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) {
     60       continue;
     61     }
     62 
     63     MbrValid    = TRUE;
     64     StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA);
     65     EndingLBA   = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1;
     66     if (EndingLBA > LastLba) {
     67       //
     68       // Compatibility Errata:
     69       //  Some systems try to hide drive space with their INT 13h driver
     70       //  This does not hide space from the OS driver. This means the MBR
     71       //  that gets created from DOS is smaller than the MBR created from
     72       //  a real OS (NT & Win98). This leads to BlockIo->LastBlock being
     73       //  wrong on some systems FDISKed by the OS.
     74       //
     75       // return FALSE since no block devices on a system are implemented
     76       // with INT 13h
     77       //
     78 
     79       DEBUG((EFI_D_INFO, "PartitionValidMbr: Bad MBR partition size EndingLBA(%1x) > LastLBA(%1x)\n", EndingLBA, LastLba));
     80 
     81       return FALSE;
     82     }
     83 
     84     for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) {
     85       if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) == 0) {
     86         continue;
     87       }
     88 
     89       NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1;
     90       if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) {
     91         //
     92         // This region overlaps with the Index1'th region
     93         //
     94         return FALSE;
     95       }
     96     }
     97   }
     98   //
     99   // None of the regions overlapped so MBR is O.K.
    100   //
    101   return MbrValid;
    102 }
    103 
    104 
    105 /**
    106   Install child handles if the Handle supports MBR format.
    107 
    108   @param[in]  This              Calling context.
    109   @param[in]  Handle            Parent Handle.
    110   @param[in]  DiskIo            Parent DiskIo interface.
    111   @param[in]  DiskIo2           Parent DiskIo2 interface.
    112   @param[in]  BlockIo           Parent BlockIo interface.
    113   @param[in]  BlockIo2          Parent BlockIo2 interface.
    114   @param[in]  DevicePath        Parent Device Path.
    115 
    116   @retval EFI_SUCCESS       A child handle was added.
    117   @retval EFI_MEDIA_CHANGED Media change was detected.
    118   @retval Others            MBR partition was not found.
    119 
    120 **/
    121 EFI_STATUS
    122 PartitionInstallMbrChildHandles (
    123   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
    124   IN  EFI_HANDLE                   Handle,
    125   IN  EFI_DISK_IO_PROTOCOL         *DiskIo,
    126   IN  EFI_DISK_IO2_PROTOCOL        *DiskIo2,
    127   IN  EFI_BLOCK_IO_PROTOCOL        *BlockIo,
    128   IN  EFI_BLOCK_IO2_PROTOCOL       *BlockIo2,
    129   IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
    130   )
    131 {
    132   EFI_STATUS                Status;
    133   MASTER_BOOT_RECORD        *Mbr;
    134   UINT32                    ExtMbrStartingLba;
    135   UINTN                     Index;
    136   HARDDRIVE_DEVICE_PATH     HdDev;
    137   HARDDRIVE_DEVICE_PATH     ParentHdDev;
    138   EFI_STATUS                Found;
    139   UINT32                    PartitionNumber;
    140   EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
    141   EFI_DEVICE_PATH_PROTOCOL  *LastDevicePathNode;
    142   UINT32                    BlockSize;
    143   UINT32                    MediaId;
    144   EFI_LBA                   LastBlock;
    145 
    146   Found           = EFI_NOT_FOUND;
    147 
    148   BlockSize = BlockIo->Media->BlockSize;
    149   MediaId   = BlockIo->Media->MediaId;
    150   LastBlock = BlockIo->Media->LastBlock;
    151 
    152   Mbr = AllocatePool (BlockSize);
    153   if (Mbr == NULL) {
    154     return Found;
    155   }
    156 
    157   Status = DiskIo->ReadDisk (
    158                      DiskIo,
    159                      MediaId,
    160                      0,
    161                      BlockSize,
    162                      Mbr
    163                      );
    164   if (EFI_ERROR (Status)) {
    165     Found = Status;
    166     goto Done;
    167   }
    168   if (!PartitionValidMbr (Mbr, LastBlock)) {
    169     goto Done;
    170   }
    171   //
    172   // We have a valid mbr - add each partition
    173   //
    174   //
    175   // Get starting and ending LBA of the parent block device.
    176   //
    177   LastDevicePathNode = NULL;
    178   ZeroMem (&ParentHdDev, sizeof (ParentHdDev));
    179   DevicePathNode = DevicePath;
    180   while (!IsDevicePathEnd (DevicePathNode)) {
    181     LastDevicePathNode  = DevicePathNode;
    182     DevicePathNode      = NextDevicePathNode (DevicePathNode);
    183   }
    184 
    185   if (LastDevicePathNode != NULL) {
    186     if (DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH &&
    187         DevicePathSubType (LastDevicePathNode) == MEDIA_HARDDRIVE_DP
    188         ) {
    189       CopyMem (&ParentHdDev, LastDevicePathNode, sizeof (ParentHdDev));
    190     } else {
    191       LastDevicePathNode = NULL;
    192     }
    193   }
    194 
    195   PartitionNumber = 1;
    196 
    197   ZeroMem (&HdDev, sizeof (HdDev));
    198   HdDev.Header.Type     = MEDIA_DEVICE_PATH;
    199   HdDev.Header.SubType  = MEDIA_HARDDRIVE_DP;
    200   SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev));
    201   HdDev.MBRType         = MBR_TYPE_PCAT;
    202   HdDev.SignatureType   = SIGNATURE_TYPE_MBR;
    203 
    204   if (LastDevicePathNode == NULL) {
    205     //
    206     // This is a MBR, add each partition
    207     //
    208     for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
    209       if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA) == 0) {
    210         //
    211         // Don't use null MBR entries
    212         //
    213         continue;
    214       }
    215 
    216       if (Mbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) {
    217         //
    218         // This is the guard MBR for the GPT. If you ever see a GPT disk with zero partitions you can get here.
    219         //  We can not produce an MBR BlockIo for this device as the MBR spans the GPT headers. So formating
    220         //  this BlockIo would corrupt the GPT structures and require a recovery that would corrupt the format
    221         //  that corrupted the GPT partition.
    222         //
    223         continue;
    224       }
    225 
    226       HdDev.PartitionNumber = PartitionNumber ++;
    227       HdDev.PartitionStart  = UNPACK_UINT32 (Mbr->Partition[Index].StartingLBA);
    228       HdDev.PartitionSize   = UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA);
    229       CopyMem (HdDev.Signature, &(Mbr->UniqueMbrSignature[0]), sizeof (Mbr->UniqueMbrSignature));
    230 
    231       Status = PartitionInstallChildHandle (
    232                 This,
    233                 Handle,
    234                 DiskIo,
    235                 DiskIo2,
    236                 BlockIo,
    237                 BlockIo2,
    238                 DevicePath,
    239                 (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,
    240                 HdDev.PartitionStart,
    241                 HdDev.PartitionStart + HdDev.PartitionSize - 1,
    242                 MBR_SIZE,
    243                 (BOOLEAN) (Mbr->Partition[Index].OSIndicator == EFI_PARTITION)
    244                 );
    245 
    246       if (!EFI_ERROR (Status)) {
    247         Found = EFI_SUCCESS;
    248       }
    249     }
    250   } else {
    251     //
    252     // It's an extended partition. Follow the extended partition
    253     // chain to get all the logical drives
    254     //
    255     ExtMbrStartingLba = 0;
    256 
    257     do {
    258 
    259       Status = DiskIo->ReadDisk (
    260                          DiskIo,
    261                          MediaId,
    262                          MultU64x32 (ExtMbrStartingLba, BlockSize),
    263                          BlockSize,
    264                          Mbr
    265                          );
    266       if (EFI_ERROR (Status)) {
    267         Found = Status;
    268         goto Done;
    269       }
    270 
    271       if (UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA) == 0) {
    272         break;
    273       }
    274 
    275       if ((Mbr->Partition[0].OSIndicator == EXTENDED_DOS_PARTITION) ||
    276           (Mbr->Partition[0].OSIndicator == EXTENDED_WINDOWS_PARTITION)) {
    277         ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA);
    278         continue;
    279       }
    280       HdDev.PartitionNumber = PartitionNumber ++;
    281       HdDev.PartitionStart  = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA) + ExtMbrStartingLba + ParentHdDev.PartitionStart;
    282       HdDev.PartitionSize   = UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA);
    283       if ((HdDev.PartitionStart + HdDev.PartitionSize - 1 >= ParentHdDev.PartitionStart + ParentHdDev.PartitionSize) ||
    284           (HdDev.PartitionStart <= ParentHdDev.PartitionStart)) {
    285         break;
    286       }
    287 
    288       //
    289       // The signature in EBR(Extended Boot Record) should always be 0.
    290       //
    291       *((UINT32 *) &HdDev.Signature[0]) = 0;
    292 
    293       Status = PartitionInstallChildHandle (
    294                  This,
    295                  Handle,
    296                  DiskIo,
    297                  DiskIo2,
    298                  BlockIo,
    299                  BlockIo2,
    300                  DevicePath,
    301                  (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,
    302                  HdDev.PartitionStart - ParentHdDev.PartitionStart,
    303                  HdDev.PartitionStart - ParentHdDev.PartitionStart + HdDev.PartitionSize - 1,
    304                  MBR_SIZE,
    305                  (BOOLEAN) (Mbr->Partition[0].OSIndicator == EFI_PARTITION)
    306                  );
    307       if (!EFI_ERROR (Status)) {
    308         Found = EFI_SUCCESS;
    309       }
    310 
    311       if ((Mbr->Partition[1].OSIndicator != EXTENDED_DOS_PARTITION) &&
    312           (Mbr->Partition[1].OSIndicator != EXTENDED_WINDOWS_PARTITION)
    313           ) {
    314         break;
    315       }
    316 
    317       ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[1].StartingLBA);
    318       //
    319       // Don't allow partition to be self referencing
    320       //
    321       if (ExtMbrStartingLba == 0) {
    322         break;
    323       }
    324     } while (ExtMbrStartingLba  < ParentHdDev.PartitionSize);
    325   }
    326 
    327 Done:
    328   FreePool (Mbr);
    329 
    330   return Found;
    331 }
    332