1 /** @file
2   Decode an El Torito formatted CD-ROM
3 
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 
16 #include "Partition.h"
17 
18 
19 /**
20   Install child handles if the Handle supports El Torito format.
21 
22   @param[in]  This        Calling context.
23   @param[in]  Handle      Parent Handle.
24   @param[in]  DiskIo      Parent DiskIo interface.
25   @param[in]  DiskIo2     Parent DiskIo2 interface.
26   @param[in]  BlockIo     Parent BlockIo interface.
27   @param[in]  BlockIo2    Parent BlockIo2 interface.
28   @param[in]  DevicePath  Parent Device Path
29 
30 
31   @retval EFI_SUCCESS         Child handle(s) was added.
32   @retval EFI_MEDIA_CHANGED   Media changed Detected.
33   @retval other               no child handle was added.
34 
35 **/
36 EFI_STATUS
PartitionInstallElToritoChildHandles(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)37 PartitionInstallElToritoChildHandles (
38   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
39   IN  EFI_HANDLE                   Handle,
40   IN  EFI_DISK_IO_PROTOCOL         *DiskIo,
41   IN  EFI_DISK_IO2_PROTOCOL        *DiskIo2,
42   IN  EFI_BLOCK_IO_PROTOCOL        *BlockIo,
43   IN  EFI_BLOCK_IO2_PROTOCOL       *BlockIo2,
44   IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
45   )
46 {
47   EFI_STATUS              Status;
48   UINT64                  VolDescriptorOffset;
49   UINT32                  Lba2KB;
50   EFI_BLOCK_IO_MEDIA      *Media;
51   CDROM_VOLUME_DESCRIPTOR *VolDescriptor;
52   ELTORITO_CATALOG        *Catalog;
53   UINTN                   Check;
54   UINTN                   Index;
55   UINTN                   BootEntry;
56   UINTN                   MaxIndex;
57   UINT16                  *CheckBuffer;
58   CDROM_DEVICE_PATH       CdDev;
59   UINT32                  SubBlockSize;
60   UINT32                  SectorCount;
61   EFI_STATUS              Found;
62   UINT32                  VolSpaceSize;
63 
64   Found         = EFI_NOT_FOUND;
65   Media         = BlockIo->Media;
66 
67   VolSpaceSize  = 0;
68 
69   //
70   // CD_ROM has the fixed block size as 2048 bytes (SIZE_2KB)
71   //
72 
73   // If the ISO image has been copied onto a different storage media
74   // then the block size might be different (eg: USB).
75   // Ensure 2048 (SIZE_2KB) is a multiple of block size
76   if (((SIZE_2KB % Media->BlockSize) != 0) || (Media->BlockSize > SIZE_2KB)) {
77     return EFI_NOT_FOUND;
78   }
79 
80   VolDescriptor = AllocatePool ((UINTN)SIZE_2KB);
81 
82   if (VolDescriptor == NULL) {
83     return EFI_NOT_FOUND;
84   }
85 
86   Catalog = (ELTORITO_CATALOG *) VolDescriptor;
87 
88   //
89   // Loop: handle one volume descriptor per time
90   //       The ISO-9660 volume descriptor starts at 32k on the media
91   //
92   for (VolDescriptorOffset = SIZE_32KB;
93        VolDescriptorOffset <= MultU64x32 (Media->LastBlock, Media->BlockSize);
94        VolDescriptorOffset += SIZE_2KB) {
95     Status = DiskIo->ReadDisk (
96                        DiskIo,
97                        Media->MediaId,
98                        VolDescriptorOffset,
99                        SIZE_2KB,
100                        VolDescriptor
101                        );
102     if (EFI_ERROR (Status)) {
103       Found = Status;
104       break;
105     }
106     //
107     // Check for valid volume descriptor signature
108     //
109     if (VolDescriptor->Unknown.Type == CDVOL_TYPE_END ||
110         CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0
111         ) {
112       //
113       // end of Volume descriptor list
114       //
115       break;
116     }
117     //
118     // Read the Volume Space Size from Primary Volume Descriptor 81-88 byte,
119     // the 32-bit numerical values is stored in Both-byte orders
120     //
121     if (VolDescriptor->PrimaryVolume.Type == CDVOL_TYPE_CODED) {
122       VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[0];
123     }
124     //
125     // Is it an El Torito volume descriptor?
126     //
127     if (CompareMem (VolDescriptor->BootRecordVolume.SystemId, CDVOL_ELTORITO_ID, sizeof (CDVOL_ELTORITO_ID) - 1) != 0) {
128       continue;
129     }
130     //
131     // Read in the boot El Torito boot catalog
132     // The LBA unit used by El Torito boot catalog is 2KB unit
133     //
134     Lba2KB = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog);
135     // Ensure the LBA (in 2KB unit) fits into our media
136     if (Lba2KB * (SIZE_2KB / Media->BlockSize) > Media->LastBlock) {
137       continue;
138     }
139 
140     Status = DiskIo->ReadDisk (
141                        DiskIo,
142                        Media->MediaId,
143                        MultU64x32 (Lba2KB, SIZE_2KB),
144                        SIZE_2KB,
145                        Catalog
146                        );
147     if (EFI_ERROR (Status)) {
148       DEBUG ((EFI_D_ERROR, "EltCheckDevice: error reading catalog %r\n", Status));
149       continue;
150     }
151     //
152     // We don't care too much about the Catalog header's contents, but we do want
153     // to make sure it looks like a Catalog header
154     //
155     if (Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG || Catalog->Catalog.Id55AA != 0xAA55) {
156       DEBUG ((EFI_D_ERROR, "EltCheckBootCatalog: El Torito boot catalog header IDs not correct\n"));
157       continue;
158     }
159 
160     Check       = 0;
161     CheckBuffer = (UINT16 *) Catalog;
162     for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) {
163       Check += CheckBuffer[Index];
164     }
165 
166     if ((Check & 0xFFFF) != 0) {
167       DEBUG ((EFI_D_ERROR, "EltCheckBootCatalog: El Torito boot catalog header checksum failed\n"));
168       continue;
169     }
170 
171     MaxIndex = Media->BlockSize / sizeof (ELTORITO_CATALOG);
172     for (Index = 1, BootEntry = 1; Index < MaxIndex; Index += 1) {
173       //
174       // Next entry
175       //
176       Catalog += 1;
177 
178       //
179       // Check this entry
180       //
181       if (Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE || Catalog->Boot.Lba == 0) {
182         continue;
183       }
184 
185       SubBlockSize  = 512;
186       SectorCount   = Catalog->Boot.SectorCount * (SIZE_2KB / Media->BlockSize);
187 
188       switch (Catalog->Boot.MediaType) {
189 
190       case ELTORITO_NO_EMULATION:
191         SubBlockSize = Media->BlockSize;
192         break;
193 
194       case ELTORITO_HARD_DISK:
195         break;
196 
197       case ELTORITO_12_DISKETTE:
198         SectorCount = 0x50 * 0x02 * 0x0F;
199         break;
200 
201       case ELTORITO_14_DISKETTE:
202         SectorCount = 0x50 * 0x02 * 0x12;
203         break;
204 
205       case ELTORITO_28_DISKETTE:
206         SectorCount = 0x50 * 0x02 * 0x24;
207         break;
208 
209       default:
210         DEBUG ((EFI_D_INIT, "EltCheckDevice: unsupported El Torito boot media type %x\n", Catalog->Boot.MediaType));
211         SectorCount   = 0;
212         SubBlockSize  = Media->BlockSize;
213         break;
214       }
215       //
216       // Create child device handle
217       //
218       CdDev.Header.Type     = MEDIA_DEVICE_PATH;
219       CdDev.Header.SubType  = MEDIA_CDROM_DP;
220       SetDevicePathNodeLength (&CdDev.Header, sizeof (CdDev));
221 
222       if (Index == 1) {
223         //
224         // This is the initial/default entry
225         //
226         BootEntry = 0;
227       }
228 
229       CdDev.BootEntry = (UINT32) BootEntry;
230       BootEntry++;
231       CdDev.PartitionStart = Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize);
232       if (SectorCount < 2) {
233         //
234         // When the SectorCount < 2, set the Partition as the whole CD.
235         //
236         if (VolSpaceSize > (Media->LastBlock + 1)) {
237           CdDev.PartitionSize = (UINT32)(Media->LastBlock - Catalog->Boot.Lba + 1);
238         } else {
239           CdDev.PartitionSize = (UINT32)(VolSpaceSize - Catalog->Boot.Lba);
240         }
241       } else {
242         CdDev.PartitionSize = DivU64x32 (
243                                 MultU64x32 (
244                                   SectorCount,
245                                   SubBlockSize
246                                   ) + Media->BlockSize - 1,
247                                 Media->BlockSize
248                                 );
249       }
250 
251       Status = PartitionInstallChildHandle (
252                 This,
253                 Handle,
254                 DiskIo,
255                 DiskIo2,
256                 BlockIo,
257                 BlockIo2,
258                 DevicePath,
259                 (EFI_DEVICE_PATH_PROTOCOL *) &CdDev,
260                 Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize),
261                 MultU64x32 (Catalog->Boot.Lba + CdDev.PartitionSize - 1, SIZE_2KB / Media->BlockSize),
262                 SubBlockSize,
263                 FALSE
264                 );
265       if (!EFI_ERROR (Status)) {
266         Found = EFI_SUCCESS;
267       }
268     }
269   }
270 
271   FreePool (VolDescriptor);
272 
273   return Found;
274 }
275