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