/* * Copyright (C) 2022-2024 Intel Corporation * * SPDX-License-Identifier: MIT * */ #include "shared/source/device_binary_format/zebin/zebin_decoder.h" #include "shared/source/compiler_interface/intermediate_representations.h" #include "shared/source/debug_settings/debug_settings_manager.h" #include "shared/source/device_binary_format/device_binary_formats.h" #include "shared/source/device_binary_format/elf/elf_decoder.h" #include "shared/source/device_binary_format/zebin/zebin_elf.h" #include "shared/source/device_binary_format/zebin/zeinfo_decoder.h" #include "shared/source/helpers/aligned_memory.h" #include "shared/source/helpers/ptr_math.h" #include "shared/source/program/kernel_info.h" #include "shared/source/program/program_info.h" #include "shared/source/utilities/logger.h" #include "platforms.h" namespace NEO { template <> bool isDeviceBinaryFormat(const ArrayRef binary) { return Zebin::isZebin(binary) || Zebin::isZebin(binary); }; namespace Zebin { void setKernelMiscInfoPosition(ConstStringRef metadata, NEO::ProgramInfo &dst) { dst.kernelMiscInfoPos = metadata.str().find(ZeInfo::Tags::kernelMiscInfo.str()); } template bool isZebin(ArrayRef binary); template bool isZebin(ArrayRef binary); template bool isZebin(ArrayRef binary) { auto fileHeader = Elf::decodeElfFileHeader(binary); return fileHeader != nullptr && (fileHeader->type == Elf::ET_REL || fileHeader->type == Elf::ET_ZEBIN_EXE); } bool isTargetProductConfigCompatibleWithProductConfig(const AOT::PRODUCT_CONFIG &targetDeviceProductConfig, const AOT::PRODUCT_CONFIG &productConfig) { auto compatProdConfPairItr = AOT::compatibilityMapping.find(productConfig); if (compatProdConfPairItr != AOT::compatibilityMapping.end()) { for (auto &compatibleConfig : compatProdConfPairItr->second) if (targetDeviceProductConfig == compatibleConfig) return true; } return false; } bool validateTargetDevice(const TargetDevice &targetDevice, Elf::ElfIdentifierClass numBits, PRODUCT_FAMILY productFamily, GFXCORE_FAMILY gfxCore, AOT::PRODUCT_CONFIG productConfig, Elf::ZebinTargetFlags targetMetadata) { if (targetDevice.maxPointerSizeInBytes == 4 && static_cast(numBits == Elf::EI_CLASS_64)) { return false; } if (productConfig != AOT::UNKNOWN_ISA) { auto targetDeviceProductConfig = static_cast(targetDevice.aotConfig.value); if (targetDeviceProductConfig == productConfig) return true; else if (debugManager.flags.EnableCompatibilityMode.get() == true) { return isTargetProductConfigCompatibleWithProductConfig(targetDeviceProductConfig, productConfig); } else return false; } if (gfxCore == IGFX_UNKNOWN_CORE && productFamily == IGFX_UNKNOWN) { return false; } if (gfxCore != IGFX_UNKNOWN_CORE) { if (targetDevice.coreFamily != gfxCore) { return false; } } if (productFamily != IGFX_UNKNOWN) { if (targetDevice.productFamily != productFamily) { return false; } } if (targetMetadata.validateRevisionId) { bool isValidStepping = (targetDevice.stepping >= targetMetadata.minHwRevisionId) && (targetDevice.stepping <= targetMetadata.maxHwRevisionId); if (false == isValidStepping) { return false; } } return true; } template bool validateTargetDevice(const Elf::Elf &elf, const TargetDevice &targetDevice, std::string &outErrReason, std::string &outWarning, SingleDeviceBinary &singleDeviceBinary); template bool validateTargetDevice(const Elf::Elf &elf, const TargetDevice &targetDevice, std::string &outErrReason, std::string &outWarning, SingleDeviceBinary &singleDeviceBinary); template bool validateTargetDevice(const Elf::Elf &elf, const TargetDevice &targetDevice, std::string &outErrReason, std::string &outWarning, SingleDeviceBinary &singleDeviceBinary) { GFXCORE_FAMILY gfxCore = IGFX_UNKNOWN_CORE; PRODUCT_FAMILY productFamily = IGFX_UNKNOWN; AOT::PRODUCT_CONFIG productConfig = AOT::UNKNOWN_ISA; Elf::ZebinTargetFlags targetMetadata = {}; std::vector intelGTNotes = {}; auto decodeError = getIntelGTNotes(elf, intelGTNotes, outErrReason, outWarning); if (DecodeError::success != decodeError) { return false; } for (const auto &intelGTNote : intelGTNotes) { switch (intelGTNote.type) { case Elf::IntelGTSectionType::productFamily: { DEBUG_BREAK_IF(sizeof(uint32_t) != intelGTNote.data.size()); auto productFamilyData = reinterpret_cast(intelGTNote.data.begin()); productFamily = static_cast(*productFamilyData); break; } case Elf::IntelGTSectionType::gfxCore: { DEBUG_BREAK_IF(sizeof(uint32_t) != intelGTNote.data.size()); auto gfxCoreData = reinterpret_cast(intelGTNote.data.begin()); gfxCore = static_cast(*gfxCoreData); break; } case Elf::IntelGTSectionType::targetMetadata: { DEBUG_BREAK_IF(sizeof(uint32_t) != intelGTNote.data.size()); auto targetMetadataPacked = reinterpret_cast(intelGTNote.data.begin()); targetMetadata.packed = static_cast(*targetMetadataPacked); singleDeviceBinary.generator = static_cast(targetMetadata.generatorId); break; } case Elf::IntelGTSectionType::zebinVersion: { auto zebinVersionData = reinterpret_cast(intelGTNote.data.begin()); ConstStringRef versionString(zebinVersionData); ZeInfo::Types::Version receivedZeInfoVersion{0, 0}; decodeError = ZeInfo::populateZeInfoVersion(receivedZeInfoVersion, versionString, outErrReason); if (DecodeError::success != decodeError) { return false; } decodeError = ZeInfo::validateZeInfoVersion(receivedZeInfoVersion, outErrReason, outWarning); if (DecodeError::success != decodeError) { return false; } break; } case Elf::IntelGTSectionType::productConfig: { if (false == targetDevice.applyValidationWorkaround) { DEBUG_BREAK_IF(sizeof(uint32_t) != intelGTNote.data.size()); auto productConfigData = reinterpret_cast(intelGTNote.data.begin()); productConfig = static_cast(*productConfigData); break; } break; } case Elf::IntelGTSectionType::vISAAbiVersion: { break; } case Elf::IntelGTSectionType::indirectAccessDetectionVersion: { DEBUG_BREAK_IF(sizeof(uint32_t) != intelGTNote.data.size()); auto indirectDetectionVersion = reinterpret_cast(intelGTNote.data.begin()); singleDeviceBinary.generatorFeatureVersions.indirectMemoryAccessDetection = static_cast(*indirectDetectionVersion); break; } default: outWarning.append("DeviceBinaryFormat::zebin : Unrecognized IntelGTNote type: " + std::to_string(intelGTNote.type) + "\n"); break; } } return validateTargetDevice(targetDevice, numBits, productFamily, gfxCore, productConfig, targetMetadata); } template DecodeError decodeIntelGTNoteSection(ArrayRef intelGTNotesSection, std::vector &intelGTNotes, std::string &outErrReason, std::string &outWarning) { uint64_t currentPos = 0; auto sectionSize = intelGTNotesSection.size(); while (currentPos < sectionSize) { auto intelGTNote = reinterpret_cast(intelGTNotesSection.begin() + currentPos); auto nameSz = intelGTNote->nameSize; auto descSz = intelGTNote->descSize; auto currOffset = sizeof(Elf::ElfNoteSection) + alignUp(nameSz, 4) + alignUp(descSz, 4); if (currentPos + currOffset > sectionSize) { intelGTNotes.clear(); outErrReason.append("DeviceBinaryFormat::zebin : Offsetting will cause out-of-bound memory read! Section size: " + std::to_string(sectionSize) + ", current section data offset: " + std::to_string(currentPos) + ", next offset : " + std::to_string(currOffset) + "\n"); return DecodeError::invalidBinary; } currentPos += currOffset; auto ownerName = reinterpret_cast(ptrOffset(intelGTNote, sizeof(Elf::ElfNoteSection))); bool isValidGTNote = Elf::intelGTNoteOwnerName.size() + 1 == nameSz; isValidGTNote &= Elf::intelGTNoteOwnerName == ConstStringRef(ownerName, nameSz - 1); if (false == isValidGTNote) { if (0u == nameSz) { outWarning.append("DeviceBinaryFormat::zebin : Empty owner name.\n"); } else { std::string invalidOwnerName{ownerName, nameSz}; invalidOwnerName.erase(std::remove_if(invalidOwnerName.begin(), invalidOwnerName.end(), [](unsigned char c) { return '\0' == c; })); outWarning.append("DeviceBinaryFormat::zebin : Invalid owner name : " + invalidOwnerName + " for IntelGTNote - note will not be used.\n"); } continue; } auto notesData = ArrayRef(reinterpret_cast(ptrOffset(ownerName, nameSz)), descSz); if (intelGTNote->type == Elf::IntelGTSectionType::zebinVersion) { isValidGTNote &= notesData[descSz - 1] == '\0'; if (false == isValidGTNote) { outWarning.append("DeviceBinaryFormat::zebin : Versioning string is not null-terminated: " + ConstStringRef(reinterpret_cast(notesData.begin()), descSz).str() + " - note will not be used.\n"); continue; } } intelGTNotes.push_back(Elf::IntelGTNote{static_cast(intelGTNote->type), notesData}); } return DecodeError::success; } template DecodeError getIntelGTNotes(const Elf::Elf &elf, std::vector &intelGTNotes, std::string &outErrReason, std::string &outWarning) { for (size_t i = 0; i < elf.sectionHeaders.size(); i++) { auto section = elf.sectionHeaders[i]; if (Elf::SHT_NOTE == section.header->type && Elf::SectionNames::noteIntelGT == elf.getSectionName(static_cast(i))) { return decodeIntelGTNoteSection(section.data, intelGTNotes, outErrReason, outWarning); } } return DecodeError::success; } template DecodeError extractZebinSections(NEO::Elf::Elf &elf, ZebinSections &out, std::string &outErrReason, std::string &outWarning) { if ((elf.elfFileHeader->shStrNdx >= elf.sectionHeaders.size()) || (NEO::Elf::SHN_UNDEF == elf.elfFileHeader->shStrNdx)) { outErrReason.append("DeviceBinaryFormat::zebin : Invalid or missing shStrNdx in elf header\n"); return DecodeError::invalidBinary; } auto sectionHeaderNamesData = elf.sectionHeaders[elf.elfFileHeader->shStrNdx].data; ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); for (auto &elfSectionHeader : elf.sectionHeaders) { ConstStringRef sectionName = ConstStringRef(sectionHeaderNamesString.begin() + elfSectionHeader.header->name); switch (elfSectionHeader.header->type) { default: outErrReason.append("DeviceBinaryFormat::zebin : Unhandled ELF section header type : " + std::to_string(elfSectionHeader.header->type) + "\n"); return DecodeError::invalidBinary; case Elf::SHT_PROGBITS: if (sectionName.startsWith(Elf::SectionNames::textPrefix.data())) { out.textKernelSections.push_back(&elfSectionHeader); } else if (sectionName == Elf::SectionNames::dataConst) { out.constDataSections.push_back(&elfSectionHeader); } else if (sectionName == Elf::SectionNames::dataGlobalConst) { outWarning.append("Misspelled section name : " + sectionName.str() + ", should be : " + Elf::SectionNames::dataConst.str() + "\n"); out.constDataSections.push_back(&elfSectionHeader); } else if (sectionName == Elf::SectionNames::dataGlobal) { out.globalDataSections.push_back(&elfSectionHeader); } else if (sectionName == Elf::SectionNames::dataConstString) { out.constDataStringSections.push_back(&elfSectionHeader); } else if (sectionName.startsWith(Elf::SectionNames::debugPrefix.data())) { // ignoring intentionally } else { outErrReason.append("DeviceBinaryFormat::zebin : Unhandled SHT_PROGBITS section : " + sectionName.str() + " currently supports only : " + Elf::SectionNames::textPrefix.str() + "KERNEL_NAME, " + Elf::SectionNames::dataConst.str() + ", " + Elf::SectionNames::dataGlobal.str() + " and " + Elf::SectionNames::debugPrefix.str() + "* .\n"); return DecodeError::invalidBinary; } break; case Elf::SHT_ZEBIN_ZEINFO: out.zeInfoSections.push_back(&elfSectionHeader); break; case NEO::Elf::SHT_SYMTAB: out.symtabSections.push_back(&elfSectionHeader); break; case Elf::SHT_ZEBIN_SPIRV: out.spirvSections.push_back(&elfSectionHeader); break; case NEO::Elf::SHT_NOTE: if (sectionName == Elf::SectionNames::noteIntelGT) { out.noteIntelGTSections.push_back(&elfSectionHeader); } else { outWarning.append("DeviceBinaryFormat::zebin : Unhandled SHT_NOTE section : " + sectionName.str() + " currently supports only : " + Elf::SectionNames::noteIntelGT.str() + ".\n"); } break; case Elf::SHT_ZEBIN_MISC: if (sectionName == Elf::SectionNames::buildOptions) { out.buildOptionsSection.push_back(&elfSectionHeader); } else { outWarning.append("DeviceBinaryFormat::zebin : unhandled SHT_ZEBIN_MISC section : " + sectionName.str() + " currently supports only : " + Elf::SectionNames::buildOptions.str() + ".\n"); } break; case NEO::Elf::SHT_STRTAB: // ignoring intentionally - section header names continue; case NEO::Elf::SHT_REL: case NEO::Elf::SHT_RELA: // ignoring intentionally - rel/rela sections handled by Elf decoder continue; case Elf::SHT_ZEBIN_GTPIN_INFO: if (sectionName.startsWith(Elf::SectionNames::gtpinInfo.data())) { out.gtpinInfoSections.push_back(&elfSectionHeader); } else { outWarning.append("DeviceBinaryFormat::zebin : Unhandled SHT_ZEBIN_GTPIN_INFO section : " + sectionName.str() + ", currently supports only : " + Elf::SectionNames::gtpinInfo.str() + "KERNEL_NAME\n"); } break; case Elf::SHT_ZEBIN_VISA_ASM: // ignoring intentionally - visa asm continue; case NEO::Elf::SHT_NULL: // ignoring intentionally, inactive section, probably UNDEF continue; case NEO::Elf::SHT_NOBITS: if (sectionName == Elf::SectionNames::dataConstZeroInit) { out.constZeroInitDataSections.push_back(&elfSectionHeader); } else if (sectionName == Elf::SectionNames::dataGlobalZeroInit) { out.globalZeroInitDataSections.push_back(&elfSectionHeader); } else { outWarning.append("DeviceBinaryFormat::zebin : unhandled SHT_NOBITS section : " + sectionName.str() + " currently supports only : " + Elf::SectionNames::dataConstZeroInit.str() + " and " + Elf::SectionNames::dataGlobalZeroInit.str() + ".\n"); } break; } } return DecodeError::success; } template bool validateZebinSectionsCountAtMost(const ContainerT §ionsContainer, ConstStringRef sectionName, uint32_t max, std::string &outErrReason, std::string &outWarning) { if (sectionsContainer.size() <= max) { return true; } outErrReason.append("DeviceBinaryFormat::zebin : Expected at most " + std::to_string(max) + " of " + sectionName.str() + " section, got : " + std::to_string(sectionsContainer.size()) + "\n"); return false; } template DecodeError validateZebinSectionsCount(const ZebinSections §ions, std::string &outErrReason, std::string &outWarning); template DecodeError validateZebinSectionsCount(const ZebinSections §ions, std::string &outErrReason, std::string &outWarning); template DecodeError validateZebinSectionsCount(const ZebinSections §ions, std::string &outErrReason, std::string &outWarning) { bool valid = validateZebinSectionsCountAtMost(sections.zeInfoSections, Elf::SectionNames::zeInfo, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.globalDataSections, Elf::SectionNames::dataGlobal, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.globalZeroInitDataSections, Elf::SectionNames::dataGlobalZeroInit, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.constDataSections, Elf::SectionNames::dataConst, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.constZeroInitDataSections, Elf::SectionNames::dataConstZeroInit, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.constDataStringSections, Elf::SectionNames::dataConstString, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.symtabSections, Elf::SectionNames::symtab, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.spirvSections, Elf::SectionNames::spv, 1U, outErrReason, outWarning); valid &= validateZebinSectionsCountAtMost(sections.noteIntelGTSections, Elf::SectionNames::noteIntelGT, 1U, outErrReason, outWarning); return valid ? DecodeError::success : DecodeError::invalidBinary; } template ConstStringRef extractZeInfoMetadataString(const ArrayRef zebin, std::string &outErrReason, std::string &outWarning) { auto decodedElf = NEO::Elf::decodeElf(zebin, outErrReason, outWarning); for (const auto §ionHeader : decodedElf.sectionHeaders) { if (sectionHeader.header->type == Elf::SHT_ZEBIN_ZEINFO) { auto zeInfoData = sectionHeader.data; return ConstStringRef{reinterpret_cast(zeInfoData.begin()), zeInfoData.size()}; } } return ConstStringRef{}; } ConstStringRef getZeInfoFromZebin(const ArrayRef zebin, std::string &outErrReason, std::string &outWarning) { return Elf::isElf(zebin) ? extractZeInfoMetadataString(zebin, outErrReason, outWarning) : extractZeInfoMetadataString(zebin, outErrReason, outWarning); } template DecodeError decodeZebin(ProgramInfo &dst, NEO::Elf::Elf &elf, std::string &outErrReason, std::string &outWarning); template DecodeError decodeZebin(ProgramInfo &dst, NEO::Elf::Elf &elf, std::string &outErrReason, std::string &outWarning); template DecodeError decodeZebin(ProgramInfo &dst, NEO::Elf::Elf &elf, std::string &outErrReason, std::string &outWarning) { ZebinSections zebinSections; auto extractError = extractZebinSections(elf, zebinSections, outErrReason, outWarning); if (DecodeError::success != extractError) { return extractError; } extractError = validateZebinSectionsCount(zebinSections, outErrReason, outWarning); if (DecodeError::success != extractError) { return extractError; } if (false == zebinSections.globalDataSections.empty()) { dst.globalVariables.initData = zebinSections.globalDataSections[0]->data.begin(); dst.globalVariables.size = zebinSections.globalDataSections[0]->data.size(); } if (false == zebinSections.globalZeroInitDataSections.empty()) { dst.globalVariables.zeroInitSize = static_cast(zebinSections.globalZeroInitDataSections[0]->header->size); } if (false == zebinSections.constDataSections.empty()) { dst.globalConstants.initData = zebinSections.constDataSections[0]->data.begin(); dst.globalConstants.size = zebinSections.constDataSections[0]->data.size(); } if (false == zebinSections.constZeroInitDataSections.empty()) { dst.globalConstants.zeroInitSize = static_cast(zebinSections.constZeroInitDataSections[0]->header->size); } if (false == zebinSections.constDataStringSections.empty()) { dst.globalStrings.initData = zebinSections.constDataStringSections[0]->data.begin(); dst.globalStrings.size = zebinSections.constDataStringSections[0]->data.size(); } if (zebinSections.zeInfoSections.empty()) { outWarning.append("DeviceBinaryFormat::zebin : Expected at least one " + Elf::SectionNames::zeInfo.str() + " section, got 0\n"); return DecodeError::success; } auto metadataSectionData = zebinSections.zeInfoSections[0]->data; ConstStringRef zeinfo(reinterpret_cast(metadataSectionData.begin()), metadataSectionData.size()); std::string logStr("\n=== ZEInfo logging begin ===\n"); logStr.append(zeinfo.str()); logStr.append("=== ZEInfo logging end ===\n"); DBG_LOG(LogZEInfo, logStr.c_str()); setKernelMiscInfoPosition(zeinfo, dst); if (std::string::npos != dst.kernelMiscInfoPos) { zeinfo = zeinfo.substr(static_cast(0), dst.kernelMiscInfoPos); } auto decodeZeInfoError = ZeInfo::decodeZeInfo(dst, zeinfo, outErrReason, outWarning); if (DecodeError::success != decodeZeInfoError) { return decodeZeInfoError; } for (auto &kernelInfo : dst.kernelInfos) { ConstStringRef kernelName(kernelInfo->kernelDescriptor.kernelMetadata.kernelName); auto kernelInstructions = getKernelHeap(kernelName, elf, zebinSections); if (kernelInstructions.empty()) { outErrReason.append("DeviceBinaryFormat::zebin : Could not find text section for kernel " + kernelName.str() + "\n"); return DecodeError::invalidBinary; } auto gtpinInfoForKernel = getKernelGtpinInfo(kernelName, elf, zebinSections); if (false == gtpinInfoForKernel.empty()) { kernelInfo->igcInfoForGtpin = reinterpret_cast(gtpinInfoForKernel.begin()); } kernelInfo->heapInfo.pKernelHeap = kernelInstructions.begin(); kernelInfo->heapInfo.kernelHeapSize = static_cast(kernelInstructions.size()); kernelInfo->heapInfo.kernelUnpaddedSize = static_cast(kernelInstructions.size()); auto &kernelSSH = kernelInfo->kernelDescriptor.generatedSsh; kernelInfo->heapInfo.pSsh = kernelSSH.data(); kernelInfo->heapInfo.surfaceStateHeapSize = static_cast(kernelSSH.size()); auto &kernelDSH = kernelInfo->kernelDescriptor.generatedDsh; kernelInfo->heapInfo.pDsh = kernelDSH.data(); kernelInfo->heapInfo.dynamicStateHeapSize = static_cast(kernelDSH.size()); } return DecodeError::success; } template ArrayRef getKernelHeap(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections); template ArrayRef getKernelHeap(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections); template ArrayRef getKernelHeap(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections) { auto sectionHeaderNamesData = elf.sectionHeaders[elf.elfFileHeader->shStrNdx].data; ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); for (auto *textSection : zebinSections.textKernelSections) { ConstStringRef sectionName = ConstStringRef(sectionHeaderNamesString.begin() + textSection->header->name); auto sufix = sectionName.substr(static_cast(Elf::SectionNames::textPrefix.length())); if (sufix == kernelName) { return textSection->data; } } return {}; } template ArrayRef getKernelGtpinInfo(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections); template ArrayRef getKernelGtpinInfo(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections); template ArrayRef getKernelGtpinInfo(ConstStringRef &kernelName, Elf::Elf &elf, const ZebinSections &zebinSections) { auto sectionHeaderNamesData = elf.sectionHeaders[elf.elfFileHeader->shStrNdx].data; ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); for (auto *gtpinInfoSection : zebinSections.gtpinInfoSections) { ConstStringRef sectionName = ConstStringRef(sectionHeaderNamesString.begin() + gtpinInfoSection->header->name); auto sufix = sectionName.substr(static_cast(Elf::SectionNames::gtpinInfo.length())); if (sufix == kernelName) { return gtpinInfoSection->data; } } return {}; } } // namespace Zebin } // namespace NEO