1 /** @file
2 Routines supporting partition discovery and
3 logical device reading
4
5 Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <IndustryStandard/Mbr.h>
18 #include <IndustryStandard/ElTorito.h>
19 #include "FatLitePeim.h"
20
21 /**
22 This function finds Eltorito partitions. Main algorithm
23 is ported from DXE partition driver.
24
25 @param PrivateData The global memory map
26 @param ParentBlockDevNo The parent block device
27
28 @retval TRUE New partitions are detected and logical block devices
29 are added to block device array
30 @retval FALSE No New partitions are added;
31
32 **/
33 BOOLEAN
34 FatFindEltoritoPartitions (
35 IN PEI_FAT_PRIVATE_DATA *PrivateData,
36 IN UINTN ParentBlockDevNo
37 );
38
39 /**
40 This function finds Mbr partitions. Main algorithm
41 is ported from DXE partition driver.
42
43 @param PrivateData The global memory map
44 @param ParentBlockDevNo The parent block device
45
46 @retval TRUE New partitions are detected and logical block devices
47 are added to block device array
48 @retval FALSE No New partitions are added;
49
50 **/
51 BOOLEAN
52 FatFindMbrPartitions (
53 IN PEI_FAT_PRIVATE_DATA *PrivateData,
54 IN UINTN ParentBlockDevNo
55 );
56
57
58 /**
59 This function finds partitions (logical devices) in physical block devices.
60
61 @param PrivateData Global memory map for accessing global variables.
62
63 **/
64 VOID
FatFindPartitions(IN PEI_FAT_PRIVATE_DATA * PrivateData)65 FatFindPartitions (
66 IN PEI_FAT_PRIVATE_DATA *PrivateData
67 )
68 {
69 BOOLEAN Found;
70 UINTN Index;
71
72 do {
73 Found = FALSE;
74
75 for (Index = 0; Index < PrivateData->BlockDeviceCount; Index++) {
76 if (!PrivateData->BlockDevice[Index].PartitionChecked) {
77 Found = FatFindMbrPartitions (PrivateData, Index);
78 if (!Found) {
79 Found = FatFindEltoritoPartitions (PrivateData, Index);
80 }
81 }
82 }
83 } while (Found && PrivateData->BlockDeviceCount <= PEI_FAT_MAX_BLOCK_DEVICE);
84 }
85
86
87 /**
88 This function finds Eltorito partitions. Main algorithm
89 is ported from DXE partition driver.
90
91 @param PrivateData The global memory map
92 @param ParentBlockDevNo The parent block device
93
94 @retval TRUE New partitions are detected and logical block devices
95 are added to block device array
96 @retval FALSE No New partitions are added;
97
98 **/
99 BOOLEAN
FatFindEltoritoPartitions(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN UINTN ParentBlockDevNo)100 FatFindEltoritoPartitions (
101 IN PEI_FAT_PRIVATE_DATA *PrivateData,
102 IN UINTN ParentBlockDevNo
103 )
104 {
105 EFI_STATUS Status;
106 BOOLEAN Found;
107 PEI_FAT_BLOCK_DEVICE *BlockDev;
108 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
109 UINT32 VolDescriptorLba;
110 UINT32 Lba;
111 CDROM_VOLUME_DESCRIPTOR *VolDescriptor;
112 ELTORITO_CATALOG *Catalog;
113 UINTN Check;
114 UINTN Index;
115 UINTN MaxIndex;
116 UINT16 *CheckBuffer;
117 UINT32 SubBlockSize;
118 UINT32 SectorCount;
119 UINT32 VolSpaceSize;
120
121 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
122 return FALSE;
123 }
124
125 Found = FALSE;
126 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
127 VolSpaceSize = 0;
128
129 //
130 // CD_ROM has the fixed block size as 2048 bytes
131 //
132 if (ParentBlockDev->BlockSize != 2048) {
133 return FALSE;
134 }
135
136 VolDescriptor = (CDROM_VOLUME_DESCRIPTOR *) PrivateData->BlockData;
137 Catalog = (ELTORITO_CATALOG *) VolDescriptor;
138
139 //
140 // the ISO-9660 volume descriptor starts at 32k on the media
141 // and CD_ROM has the fixed block size as 2048 bytes, so...
142 //
143 VolDescriptorLba = 15;
144 //
145 // ((16*2048) / Media->BlockSize) - 1;
146 //
147 // Loop: handle one volume descriptor per time
148 //
149 while (TRUE) {
150
151 VolDescriptorLba += 1;
152 if (VolDescriptorLba > ParentBlockDev->LastBlock) {
153 //
154 // We are pointing past the end of the device so exit
155 //
156 break;
157 }
158
159 Status = FatReadBlock (
160 PrivateData,
161 ParentBlockDevNo,
162 VolDescriptorLba,
163 ParentBlockDev->BlockSize,
164 VolDescriptor
165 );
166 if (EFI_ERROR (Status)) {
167 break;
168 }
169 //
170 // Check for valid volume descriptor signature
171 //
172 if (VolDescriptor->Unknown.Type == CDVOL_TYPE_END ||
173 CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0
174 ) {
175 //
176 // end of Volume descriptor list
177 //
178 break;
179 }
180 //
181 // Read the Volume Space Size from Primary Volume Descriptor 81-88 byte
182 //
183 if (VolDescriptor->Unknown.Type == CDVOL_TYPE_CODED) {
184 VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[1];
185 }
186 //
187 // Is it an El Torito volume descriptor?
188 //
189 if (CompareMem (
190 VolDescriptor->BootRecordVolume.SystemId,
191 CDVOL_ELTORITO_ID,
192 sizeof (CDVOL_ELTORITO_ID) - 1
193 ) != 0) {
194 continue;
195 }
196 //
197 // Read in the boot El Torito boot catalog
198 //
199 Lba = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog);
200 if (Lba > ParentBlockDev->LastBlock) {
201 continue;
202 }
203
204 Status = FatReadBlock (
205 PrivateData,
206 ParentBlockDevNo,
207 Lba,
208 ParentBlockDev->BlockSize,
209 Catalog
210 );
211 if (EFI_ERROR (Status)) {
212 continue;
213 }
214 //
215 // We don't care too much about the Catalog header's contents, but we do want
216 // to make sure it looks like a Catalog header
217 //
218 if (Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG || Catalog->Catalog.Id55AA != 0xAA55) {
219 continue;
220 }
221
222 Check = 0;
223 CheckBuffer = (UINT16 *) Catalog;
224 for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) {
225 Check += CheckBuffer[Index];
226 }
227
228 if ((Check & 0xFFFF) != 0) {
229 continue;
230 }
231
232 MaxIndex = ParentBlockDev->BlockSize / sizeof (ELTORITO_CATALOG);
233 for (Index = 1; Index < MaxIndex; Index += 1) {
234 //
235 // Next entry
236 //
237 Catalog += 1;
238
239 //
240 // Check this entry
241 //
242 if (Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE || Catalog->Boot.Lba == 0) {
243 continue;
244 }
245
246 SubBlockSize = 512;
247 SectorCount = Catalog->Boot.SectorCount;
248
249 switch (Catalog->Boot.MediaType) {
250
251 case ELTORITO_NO_EMULATION:
252 SubBlockSize = ParentBlockDev->BlockSize;
253 SectorCount = Catalog->Boot.SectorCount;
254 break;
255
256 case ELTORITO_HARD_DISK:
257 break;
258
259 case ELTORITO_12_DISKETTE:
260 SectorCount = 0x50 * 0x02 * 0x0F;
261 break;
262
263 case ELTORITO_14_DISKETTE:
264 SectorCount = 0x50 * 0x02 * 0x12;
265 break;
266
267 case ELTORITO_28_DISKETTE:
268 SectorCount = 0x50 * 0x02 * 0x24;
269 break;
270
271 default:
272 SectorCount = 0;
273 SubBlockSize = ParentBlockDev->BlockSize;
274 break;
275 }
276
277 if (SectorCount < 2) {
278 SectorCount = (VolSpaceSize > ParentBlockDev->LastBlock + 1) ? (UINT32) (ParentBlockDev->LastBlock - Catalog->Boot.Lba + 1) : (UINT32) (VolSpaceSize - Catalog->Boot.Lba);
279 }
280 //
281 // Register this partition
282 //
283 if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) {
284
285 Found = TRUE;
286
287 BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
288
289 BlockDev->BlockSize = SubBlockSize;
290 BlockDev->LastBlock = SectorCount - 1;
291 BlockDev->IoAlign = ParentBlockDev->IoAlign;
292 BlockDev->Logical = TRUE;
293 BlockDev->PartitionChecked = FALSE;
294 BlockDev->StartingPos = MultU64x32 (Catalog->Boot.Lba, ParentBlockDev->BlockSize);
295 BlockDev->ParentDevNo = ParentBlockDevNo;
296
297 PrivateData->BlockDeviceCount++;
298 }
299 }
300 }
301
302 ParentBlockDev->PartitionChecked = TRUE;
303
304 return Found;
305
306 }
307
308
309 /**
310 Test to see if the Mbr buffer is a valid MBR
311
312 @param Mbr Parent Handle
313 @param LastLba Last Lba address on the device.
314
315 @retval TRUE Mbr is a Valid MBR
316 @retval FALSE Mbr is not a Valid MBR
317
318 **/
319 BOOLEAN
PartitionValidMbr(IN MASTER_BOOT_RECORD * Mbr,IN EFI_PEI_LBA LastLba)320 PartitionValidMbr (
321 IN MASTER_BOOT_RECORD *Mbr,
322 IN EFI_PEI_LBA LastLba
323 )
324 {
325 UINT32 StartingLBA;
326 UINT32 EndingLBA;
327 UINT32 NewEndingLBA;
328 INTN Index1;
329 INTN Index2;
330 BOOLEAN MbrValid;
331
332 if (Mbr->Signature != MBR_SIGNATURE) {
333 return FALSE;
334 }
335 //
336 // The BPB also has this signature, so it can not be used alone.
337 //
338 MbrValid = FALSE;
339 for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) {
340 if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) {
341 continue;
342 }
343
344 MbrValid = TRUE;
345 StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA);
346 EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1;
347 if (EndingLBA > LastLba) {
348 //
349 // Compatability Errata:
350 // Some systems try to hide drive space with thier INT 13h driver
351 // This does not hide space from the OS driver. This means the MBR
352 // that gets created from DOS is smaller than the MBR created from
353 // a real OS (NT & Win98). This leads to BlockIo->LastBlock being
354 // wrong on some systems FDISKed by the OS.
355 //
356 // return FALSE Because no block devices on a system are implemented
357 // with INT 13h
358 //
359 return FALSE;
360 }
361
362 for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) {
363 if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index2].SizeInLBA) == 0) {
364 continue;
365 }
366
367 NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1;
368 if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) {
369 //
370 // This region overlaps with the Index1'th region
371 //
372 return FALSE;
373 }
374 }
375 }
376 //
377 // Non of the regions overlapped so MBR is O.K.
378 //
379 return MbrValid;
380 }
381
382
383 /**
384 This function finds Mbr partitions. Main algorithm
385 is ported from DXE partition driver.
386
387 @param PrivateData The global memory map
388 @param ParentBlockDevNo The parent block device
389
390 @retval TRUE New partitions are detected and logical block devices
391 are added to block device array
392 @retval FALSE No New partitions are added;
393
394 **/
395 BOOLEAN
FatFindMbrPartitions(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN UINTN ParentBlockDevNo)396 FatFindMbrPartitions (
397 IN PEI_FAT_PRIVATE_DATA *PrivateData,
398 IN UINTN ParentBlockDevNo
399 )
400 {
401 EFI_STATUS Status;
402 MASTER_BOOT_RECORD *Mbr;
403 UINTN Index;
404 BOOLEAN Found;
405 PEI_FAT_BLOCK_DEVICE *ParentBlockDev;
406 PEI_FAT_BLOCK_DEVICE *BlockDev;
407
408 if (ParentBlockDevNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
409 return FALSE;
410 }
411
412 ParentBlockDev = &(PrivateData->BlockDevice[ParentBlockDevNo]);
413
414 Found = FALSE;
415 Mbr = (MASTER_BOOT_RECORD *) PrivateData->BlockData;
416
417 Status = FatReadBlock (
418 PrivateData,
419 ParentBlockDevNo,
420 0,
421 ParentBlockDev->BlockSize,
422 Mbr
423 );
424
425 if (EFI_ERROR (Status) || !PartitionValidMbr (Mbr, ParentBlockDev->LastBlock)) {
426 goto Done;
427 }
428 //
429 // We have a valid mbr - add each partition
430 //
431 for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) {
432 if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) == 0) {
433 //
434 // Don't use null MBR entries
435 //
436 continue;
437 }
438 //
439 // Register this partition
440 //
441 if (PrivateData->BlockDeviceCount < PEI_FAT_MAX_BLOCK_DEVICE) {
442
443 Found = TRUE;
444
445 BlockDev = &(PrivateData->BlockDevice[PrivateData->BlockDeviceCount]);
446
447 BlockDev->BlockSize = MBR_SIZE;
448 BlockDev->LastBlock = UNPACK_INT32 (Mbr->Partition[Index].SizeInLBA) - 1;
449 BlockDev->IoAlign = ParentBlockDev->IoAlign;
450 BlockDev->Logical = TRUE;
451 BlockDev->PartitionChecked = FALSE;
452 BlockDev->StartingPos = MultU64x32 (
453 UNPACK_INT32 (Mbr->Partition[Index].StartingLBA),
454 ParentBlockDev->BlockSize
455 );
456 BlockDev->ParentDevNo = ParentBlockDevNo;
457
458 PrivateData->BlockDeviceCount++;
459 }
460 }
461
462 Done:
463
464 ParentBlockDev->PartitionChecked = TRUE;
465 return Found;
466 }
467