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
14Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
15Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
16This program and the accompanying materials
17are licensed and made available under the terms and conditions of the BSD License
18which accompanies this distribution.  The full text of the license may be found at
19http://opensource.org/licenses/bsd-license.php
20
21THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
22WITHOUT 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**/
38BOOLEAN
39PartitionValidMbr (
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**/
121EFI_STATUS
122PartitionInstallMbrChildHandles (
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  UINT32                    Index;
136  HARDDRIVE_DEVICE_PATH     HdDev;
137  HARDDRIVE_DEVICE_PATH     ParentHdDev;
138  EFI_STATUS                Found;
139  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
140  EFI_DEVICE_PATH_PROTOCOL  *LastDevicePathNode;
141  UINT32                    BlockSize;
142  UINT32                    MediaId;
143  EFI_LBA                   LastBlock;
144
145  Found           = EFI_NOT_FOUND;
146
147  BlockSize = BlockIo->Media->BlockSize;
148  MediaId   = BlockIo->Media->MediaId;
149  LastBlock = BlockIo->Media->LastBlock;
150
151  Mbr = AllocatePool (BlockSize);
152  if (Mbr == NULL) {
153    return Found;
154  }
155
156  Status = DiskIo->ReadDisk (
157                     DiskIo,
158                     MediaId,
159                     0,
160                     BlockSize,
161                     Mbr
162                     );
163  if (EFI_ERROR (Status)) {
164    Found = Status;
165    goto Done;
166  }
167  if (!PartitionValidMbr (Mbr, LastBlock)) {
168    goto Done;
169  }
170  //
171  // We have a valid mbr - add each partition
172  //
173  //
174  // Get starting and ending LBA of the parent block device.
175  //
176  LastDevicePathNode = NULL;
177  ZeroMem (&ParentHdDev, sizeof (ParentHdDev));
178  DevicePathNode = DevicePath;
179  while (!IsDevicePathEnd (DevicePathNode)) {
180    LastDevicePathNode  = DevicePathNode;
181    DevicePathNode      = NextDevicePathNode (DevicePathNode);
182  }
183
184  if (LastDevicePathNode != NULL) {
185    if (DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH &&
186        DevicePathSubType (LastDevicePathNode) == MEDIA_HARDDRIVE_DP
187        ) {
188      CopyMem (&ParentHdDev, LastDevicePathNode, sizeof (ParentHdDev));
189    } else {
190      LastDevicePathNode = NULL;
191    }
192  }
193
194  ZeroMem (&HdDev, sizeof (HdDev));
195  HdDev.Header.Type     = MEDIA_DEVICE_PATH;
196  HdDev.Header.SubType  = MEDIA_HARDDRIVE_DP;
197  SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev));
198  HdDev.MBRType         = MBR_TYPE_PCAT;
199  HdDev.SignatureType   = SIGNATURE_TYPE_MBR;
200
201  if (LastDevicePathNode == NULL) {
202    //
203    // This is a MBR, add each partition
204    //
205    for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
206      if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA) == 0) {
207        //
208        // Don't use null MBR entries
209        //
210        continue;
211      }
212
213      if (Mbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) {
214        //
215        // This is the guard MBR for the GPT. If you ever see a GPT disk with zero partitions you can get here.
216        //  We can not produce an MBR BlockIo for this device as the MBR spans the GPT headers. So formating
217        //  this BlockIo would corrupt the GPT structures and require a recovery that would corrupt the format
218        //  that corrupted the GPT partition.
219        //
220        continue;
221      }
222
223      HdDev.PartitionNumber = Index + 1;
224      HdDev.PartitionStart  = UNPACK_UINT32 (Mbr->Partition[Index].StartingLBA);
225      HdDev.PartitionSize   = UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA);
226      CopyMem (HdDev.Signature, &(Mbr->UniqueMbrSignature[0]), sizeof (Mbr->UniqueMbrSignature));
227
228      Status = PartitionInstallChildHandle (
229                This,
230                Handle,
231                DiskIo,
232                DiskIo2,
233                BlockIo,
234                BlockIo2,
235                DevicePath,
236                (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,
237                HdDev.PartitionStart,
238                HdDev.PartitionStart + HdDev.PartitionSize - 1,
239                MBR_SIZE,
240                (BOOLEAN) (Mbr->Partition[Index].OSIndicator == EFI_PARTITION)
241                );
242
243      if (!EFI_ERROR (Status)) {
244        Found = EFI_SUCCESS;
245      }
246    }
247  } else {
248    //
249    // It's an extended partition. Follow the extended partition
250    // chain to get all the logical drives
251    //
252    Index             = 0;
253    ExtMbrStartingLba = 0;
254
255    do {
256
257      Status = DiskIo->ReadDisk (
258                         DiskIo,
259                         MediaId,
260                         MultU64x32 (ExtMbrStartingLba, BlockSize),
261                         BlockSize,
262                         Mbr
263                         );
264      if (EFI_ERROR (Status)) {
265        Found = Status;
266        goto Done;
267      }
268
269      if (UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA) == 0) {
270        break;
271      }
272
273      if ((Mbr->Partition[0].OSIndicator == EXTENDED_DOS_PARTITION) ||
274          (Mbr->Partition[0].OSIndicator == EXTENDED_WINDOWS_PARTITION)) {
275        ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA);
276        continue;
277      }
278      HdDev.PartitionNumber = ++Index;
279      HdDev.PartitionStart  = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA) + ExtMbrStartingLba + ParentHdDev.PartitionStart;
280      HdDev.PartitionSize   = UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA);
281      if ((HdDev.PartitionStart + HdDev.PartitionSize - 1 >= ParentHdDev.PartitionStart + ParentHdDev.PartitionSize) ||
282          (HdDev.PartitionStart <= ParentHdDev.PartitionStart)) {
283        break;
284      }
285
286      //
287      // The signature in EBR(Extended Boot Record) should always be 0.
288      //
289      *((UINT32 *) &HdDev.Signature[0]) = 0;
290
291      Status = PartitionInstallChildHandle (
292                 This,
293                 Handle,
294                 DiskIo,
295                 DiskIo2,
296                 BlockIo,
297                 BlockIo2,
298                 DevicePath,
299                 (EFI_DEVICE_PATH_PROTOCOL *) &HdDev,
300                 HdDev.PartitionStart - ParentHdDev.PartitionStart,
301                 HdDev.PartitionStart - ParentHdDev.PartitionStart + HdDev.PartitionSize - 1,
302                 MBR_SIZE,
303                 (BOOLEAN) (Mbr->Partition[0].OSIndicator == EFI_PARTITION)
304                 );
305      if (!EFI_ERROR (Status)) {
306        Found = EFI_SUCCESS;
307      }
308
309      if ((Mbr->Partition[1].OSIndicator != EXTENDED_DOS_PARTITION) &&
310          (Mbr->Partition[1].OSIndicator != EXTENDED_WINDOWS_PARTITION)
311          ) {
312        break;
313      }
314
315      ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[1].StartingLBA);
316      //
317      // Don't allow partition to be self referencing
318      //
319      if (ExtMbrStartingLba == 0) {
320        break;
321      }
322    } while (ExtMbrStartingLba  < ParentHdDev.PartitionSize);
323  }
324
325Done:
326  FreePool (Mbr);
327
328  return Found;
329}
330