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