| /** @file | |
| Decode an El Torito formatted CD-ROM | |
| Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. | |
| Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Partition.h" | |
| /** | |
| Install child handles if the Handle supports El Torito format. | |
| @param[in] This Calling context. | |
| @param[in] Handle Parent Handle. | |
| @param[in] DiskIo Parent DiskIo interface. | |
| @param[in] DiskIo2 Parent DiskIo2 interface. | |
| @param[in] BlockIo Parent BlockIo interface. | |
| @param[in] BlockIo2 Parent BlockIo2 interface. | |
| @param[in] DevicePath Parent Device Path | |
| @retval EFI_SUCCESS Child handle(s) was added. | |
| @retval EFI_MEDIA_CHANGED Media changed Detected. | |
| @retval other no child handle was added. | |
| **/ | |
| 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 | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 VolDescriptorOffset; | |
| UINT32 Lba2KB; | |
| EFI_BLOCK_IO_MEDIA *Media; | |
| CDROM_VOLUME_DESCRIPTOR *VolDescriptor; | |
| ELTORITO_CATALOG *Catalog; | |
| UINTN Check; | |
| UINTN Index; | |
| UINTN BootEntry; | |
| UINTN MaxIndex; | |
| UINT16 *CheckBuffer; | |
| CDROM_DEVICE_PATH CdDev; | |
| UINT32 SubBlockSize; | |
| UINT32 SectorCount; | |
| EFI_STATUS Found; | |
| UINT32 VolSpaceSize; | |
| EFI_PARTITION_INFO_PROTOCOL PartitionInfo; | |
| Found = EFI_NOT_FOUND; | |
| Media = BlockIo->Media; | |
| VolSpaceSize = 0; | |
| // | |
| // CD_ROM has the fixed block size as 2048 bytes (SIZE_2KB) | |
| // | |
| // If the ISO image has been copied onto a different storage media | |
| // then the block size might be different (eg: USB). | |
| // Ensure 2048 (SIZE_2KB) is a multiple of block size | |
| if (((SIZE_2KB % Media->BlockSize) != 0) || (Media->BlockSize > SIZE_2KB)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| VolDescriptor = AllocatePool ((UINTN)SIZE_2KB); | |
| if (VolDescriptor == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| Catalog = (ELTORITO_CATALOG *)VolDescriptor; | |
| // | |
| // Loop: handle one volume descriptor per time | |
| // The ISO-9660 volume descriptor starts at 32k on the media | |
| // | |
| for (VolDescriptorOffset = SIZE_32KB; | |
| VolDescriptorOffset <= MultU64x32 (Media->LastBlock, Media->BlockSize); | |
| VolDescriptorOffset += SIZE_2KB) | |
| { | |
| Status = DiskIo->ReadDisk ( | |
| DiskIo, | |
| Media->MediaId, | |
| VolDescriptorOffset, | |
| SIZE_2KB, | |
| VolDescriptor | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Found = Status; | |
| break; | |
| } | |
| // | |
| // Check for valid volume descriptor signature | |
| // | |
| if ((VolDescriptor->Unknown.Type == CDVOL_TYPE_END) || | |
| (CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0) | |
| ) | |
| { | |
| // | |
| // end of Volume descriptor list | |
| // | |
| break; | |
| } | |
| // | |
| // Read the Volume Space Size from Primary Volume Descriptor 81-88 byte, | |
| // the 32-bit numerical values is stored in Both-byte orders | |
| // | |
| if (VolDescriptor->PrimaryVolume.Type == CDVOL_TYPE_CODED) { | |
| VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[0]; | |
| } | |
| // | |
| // Is it an El Torito volume descriptor? | |
| // | |
| if (CompareMem (VolDescriptor->BootRecordVolume.SystemId, CDVOL_ELTORITO_ID, sizeof (CDVOL_ELTORITO_ID) - 1) != 0) { | |
| continue; | |
| } | |
| // | |
| // Read in the boot El Torito boot catalog | |
| // The LBA unit used by El Torito boot catalog is 2KB unit | |
| // | |
| Lba2KB = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog); | |
| // Ensure the LBA (in 2KB unit) fits into our media | |
| if (Lba2KB * (SIZE_2KB / Media->BlockSize) > Media->LastBlock) { | |
| continue; | |
| } | |
| Status = DiskIo->ReadDisk ( | |
| DiskIo, | |
| Media->MediaId, | |
| MultU64x32 (Lba2KB, SIZE_2KB), | |
| SIZE_2KB, | |
| Catalog | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "EltCheckDevice: error reading catalog %r\n", Status)); | |
| continue; | |
| } | |
| // | |
| // We don't care too much about the Catalog header's contents, but we do want | |
| // to make sure it looks like a Catalog header | |
| // | |
| if ((Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG) || (Catalog->Catalog.Id55AA != 0xAA55)) { | |
| DEBUG ((DEBUG_ERROR, "EltCheckBootCatalog: El Torito boot catalog header IDs not correct\n")); | |
| continue; | |
| } | |
| Check = 0; | |
| CheckBuffer = (UINT16 *)Catalog; | |
| for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) { | |
| Check += CheckBuffer[Index]; | |
| } | |
| if ((Check & 0xFFFF) != 0) { | |
| DEBUG ((DEBUG_ERROR, "EltCheckBootCatalog: El Torito boot catalog header checksum failed\n")); | |
| continue; | |
| } | |
| MaxIndex = Media->BlockSize / sizeof (ELTORITO_CATALOG); | |
| for (Index = 1, BootEntry = 1; Index < MaxIndex; Index += 1) { | |
| // | |
| // Next entry | |
| // | |
| Catalog += 1; | |
| // | |
| // Check this entry | |
| // | |
| if ((Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE) || (Catalog->Boot.Lba == 0)) { | |
| continue; | |
| } | |
| SubBlockSize = 512; | |
| SectorCount = Catalog->Boot.SectorCount; | |
| switch (Catalog->Boot.MediaType) { | |
| case ELTORITO_NO_EMULATION: | |
| SubBlockSize = Media->BlockSize; | |
| break; | |
| case ELTORITO_HARD_DISK: | |
| break; | |
| case ELTORITO_12_DISKETTE: | |
| SectorCount = 0x50 * 0x02 * 0x0F; | |
| break; | |
| case ELTORITO_14_DISKETTE: | |
| SectorCount = 0x50 * 0x02 * 0x12; | |
| break; | |
| case ELTORITO_28_DISKETTE: | |
| SectorCount = 0x50 * 0x02 * 0x24; | |
| break; | |
| default: | |
| DEBUG ((DEBUG_INIT, "EltCheckDevice: unsupported El Torito boot media type %x\n", Catalog->Boot.MediaType)); | |
| SectorCount = 0; | |
| SubBlockSize = Media->BlockSize; | |
| break; | |
| } | |
| // | |
| // Create child device handle | |
| // | |
| CdDev.Header.Type = MEDIA_DEVICE_PATH; | |
| CdDev.Header.SubType = MEDIA_CDROM_DP; | |
| SetDevicePathNodeLength (&CdDev.Header, sizeof (CdDev)); | |
| if (Index == 1) { | |
| // | |
| // This is the initial/default entry | |
| // | |
| BootEntry = 0; | |
| } | |
| CdDev.BootEntry = (UINT32)BootEntry; | |
| BootEntry++; | |
| CdDev.PartitionStart = Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize); | |
| if (SectorCount < 2) { | |
| // | |
| // When the SectorCount < 2, set the Partition as the whole CD. | |
| // | |
| if (VolSpaceSize * (SIZE_2KB / Media->BlockSize) > (Media->LastBlock + 1)) { | |
| CdDev.PartitionSize = (UINT32)(Media->LastBlock - Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize) + 1); | |
| } else { | |
| CdDev.PartitionSize = (UINT32)(VolSpaceSize - Catalog->Boot.Lba) * (SIZE_2KB / Media->BlockSize); | |
| } | |
| } else { | |
| CdDev.PartitionSize = DivU64x32 ( | |
| MultU64x32 ( | |
| SectorCount * (SIZE_2KB / Media->BlockSize), | |
| SubBlockSize | |
| ) + Media->BlockSize - 1, | |
| Media->BlockSize | |
| ); | |
| } | |
| ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); | |
| PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; | |
| PartitionInfo.Type = PARTITION_TYPE_OTHER; | |
| Status = PartitionInstallChildHandle ( | |
| This, | |
| Handle, | |
| DiskIo, | |
| DiskIo2, | |
| BlockIo, | |
| BlockIo2, | |
| DevicePath, | |
| (EFI_DEVICE_PATH_PROTOCOL *)&CdDev, | |
| &PartitionInfo, | |
| Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize), | |
| Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize) + CdDev.PartitionSize - 1, | |
| SubBlockSize, | |
| NULL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Found = EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| FreePool (VolDescriptor); | |
| return Found; | |
| } |