/*++ Copyright (c) 2014 Minoca Corp. All Rights Reserved Module Name: partelto.c Abstract: This module implements support for parsing El Torito partitions. Author: Evan Green 20-Mar-2014 Environment: Firmware --*/ // // ------------------------------------------------------------------- Includes // #include "ueficore.h" #include "part.h" // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // EFI_STATUS EfiPartitionDetectElTorito ( EFI_DRIVER_BINDING_PROTOCOL *This, EFI_HANDLE Handle, EFI_DISK_IO_PROTOCOL *DiskIo, EFI_BLOCK_IO_PROTOCOL *BlockIo, EFI_DEVICE_PATH_PROTOCOL *DevicePath ) /*++ Routine Description: This routine attempts to detect an El Torito partitioned disk, and exposes child block devices for each partition it finds. Arguments: This - Supplies the driver binding protocol instance. Handle - Supplies the new controller handle. DiskIo - Supplies a pointer to the disk I/O protocol. BlockIo - Supplies a pointer to the block I/O protocol. DevicePath - Supplies a pointer to the device path. Return Value: EFI status code. --*/ { UINTN BootEntry; EFI_ELTORITO_CATALOG *Catalog; CDROM_DEVICE_PATH CdPath; UINTN Check; UINT16 *CheckBuffer; INTN CompareResult; EFI_STATUS Found; UINTN Index; UINT32 Lba; UINTN MaxIndex; EFI_BLOCK_IO_MEDIA *Media; UINT32 SectorCount; EFI_STATUS Status; UINT32 SubBlockSize; EFI_CDROM_VOLUME_DESCRIPTOR *VolumeDescriptor; UINT32 VolumeDescriptorLba; UINT32 VolumeSpaceSize; Found = EFI_NOT_FOUND; Media = BlockIo->Media; VolumeSpaceSize = 0; // // CD-ROMs have a fixed block size. // if (Media->BlockSize != EFI_CD_BLOCK_SIZE) { return EFI_NOT_FOUND; } VolumeDescriptor = EfiCoreAllocateBootPool((UINTN)(Media->BlockSize)); if (VolumeDescriptor == NULL) { return EFI_NOT_FOUND; } Catalog = (EFI_ELTORITO_CATALOG *)VolumeDescriptor; // // The ISO-9660 volume descriptor starts at 32k on the media (and the // block size is fixed to 2048 remember). // VolumeDescriptorLba = EFI_CD_VOLUME_RECORD_LBA - 1; // // Loop over all the volumes. // while (TRUE) { VolumeDescriptorLba += 1; if (VolumeDescriptorLba > Media->LastBlock) { break; } Status = DiskIo->ReadDisk(DiskIo, Media->MediaId, VolumeDescriptorLba * Media->BlockSize, Media->BlockSize, VolumeDescriptor); if (EFI_ERROR(Status)) { RtlDebugPrint("ElTorito: Failed to read volume descriptor.\n"); Found = Status; break; } // // Check for a valid volume descriptor signature. // if (VolumeDescriptor->BootRecordVolume.Type == EFI_CD_VOLUME_TYPE_END) { break; } CompareResult = EfiCoreCompareMemory( VolumeDescriptor->BootRecordVolume.SystemId, EFI_CD_VOLUME_ELTORITO_ID, sizeof(EFI_CD_VOLUME_ELTORITO_ID) - 1); if (CompareResult != 0) { continue; } // // Read in the boot catalog. // Lba = EFI_UNPACK_UINT32(VolumeDescriptor->BootRecordVolume.Catalog); if (Lba > Media->LastBlock) { continue; } Status = DiskIo->ReadDisk(DiskIo, Media->MediaId, Lba * Media->BlockSize, Media->BlockSize, Catalog); if (EFI_ERROR(Status)) { RtlDebugPrint("ElTorito: Error reading catalog at lba %I64x.\n", Lba); continue; } // // Make sure it looks like a catalog. // if ((Catalog->Catalog.Indicator != EFI_ELTORITO_ID_CATALOG) || (Catalog->Catalog.Id55AA != 0xAA55)) { RtlDebugPrint("ElTorito: Bad catalog.\n"); continue; } Check = 0; CheckBuffer = (UINT16 *)Catalog; for (Index = 0; Index < sizeof(EFI_ELTORITO_CATALOG) / sizeof(UINT16); Index += 1) { Check += CheckBuffer[Index]; } if ((Check & 0xFFFF) != 0) { RtlDebugPrint("ElTorito: Catalog checksum failure.\n"); } MaxIndex = Media->BlockSize / sizeof(EFI_ELTORITO_CATALOG); BootEntry = 1; for (Index = 1; Index < MaxIndex; Index += 1) { Catalog += 1; if ((Catalog->Boot.Indicator != EFI_ELTORITO_ID_SECTION_BOOTABLE) || (Catalog->Boot.Lba == 0)) { continue; } SubBlockSize = 512; SectorCount = Catalog->Boot.SectorCount; switch (Catalog->Boot.MediaType) { case EFI_ELTORITO_NO_EMULATION: SubBlockSize = Media->BlockSize; break; case EFI_ELTORITO_HARD_DISK: break; case EFI_ELTORITO_12_DISKETTE: SectorCount = 0x50 * 0x02 * 0x0F; break; case EFI_ELTORITO_14_DISKETTE: SectorCount = 0x50 * 0x02 * 0x12; break; case EFI_ELTORITO_28_DISKETTE: SectorCount = 0x50 * 0x02 * 0x24; break; default: RtlDebugPrint("ElTorito: Unsupported boot media type %x.\n", Catalog->Boot.MediaType); break; } // // Create a child device handle. // CdPath.Header.Type = MEDIA_DEVICE_PATH; CdPath.Header.SubType = MEDIA_CDROM_DP; EfiCoreSetDevicePathNodeLength(&(CdPath.Header), sizeof(CdPath)); if (Index == 1) { BootEntry = 0; } CdPath.BootEntry = (UINT32)BootEntry; BootEntry += 1; CdPath.PartitionStart = Catalog->Boot.Lba; // // If the sector count is less than two, set the partition as the // whole CD. // if (SectorCount < 2) { if (VolumeSpaceSize > Media->LastBlock + 1) { CdPath.PartitionSize = (UINT32)(Media->LastBlock - Catalog->Boot.Lba + 1); } else { CdPath.PartitionSize = (UINT32)(VolumeSpaceSize - Catalog->Boot.Lba); } } else { CdPath.PartitionSize = ALIGN_VALUE(SectorCount * SubBlockSize, Media->BlockSize); } Status = EfiPartitionInstallChildHandle( This, Handle, DiskIo, BlockIo, DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&CdPath, Catalog->Boot.Lba, Catalog->Boot.Lba + CdPath.PartitionSize - 1, SubBlockSize, FALSE); if (!EFI_ERROR(Status)) { Found = EFI_SUCCESS; } } } EfiFreePool(VolumeDescriptor); return Found; } // // --------------------------------------------------------- Internal Functions //