• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 - 2016, 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
PartitionValidMbr(IN MASTER_BOOT_RECORD * Mbr,IN EFI_LBA LastLba)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
PartitionInstallMbrChildHandles(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Handle,IN EFI_DISK_IO_PROTOCOL * DiskIo,IN EFI_DISK_IO2_PROTOCOL * DiskIo2,IN EFI_BLOCK_IO_PROTOCOL * BlockIo,IN EFI_BLOCK_IO2_PROTOCOL * BlockIo2,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)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   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 
325 Done:
326   FreePool (Mbr);
327 
328   return Found;
329 }
330