/** @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; | |
} |