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