|  | #include "package_parser.hpp" | 
|  |  | 
|  | #include <libpldm/firmware_update.h> | 
|  | #include <libpldm/utils.h> | 
|  |  | 
|  | #include <phosphor-logging/lg2.hpp> | 
|  | #include <xyz/openbmc_project/Common/error.hpp> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | using namespace std; | 
|  |  | 
|  | PHOSPHOR_LOG2_USING; | 
|  |  | 
|  | namespace pldm | 
|  | { | 
|  |  | 
|  | namespace utils | 
|  | { | 
|  |  | 
|  | std::string toString(const struct variable_field& var) | 
|  | { | 
|  | if (var.ptr == nullptr || !var.length) | 
|  | { | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | std::string str(reinterpret_cast<const char*>(var.ptr), var.length); | 
|  | std::replace_if( | 
|  | str.begin(), str.end(), [](const char& c) { return !isprint(c); }, ' '); | 
|  | return str; | 
|  | } | 
|  |  | 
|  | } // namespace utils | 
|  |  | 
|  | namespace fw_update | 
|  | { | 
|  |  | 
|  | using InternalFailure = | 
|  | sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; | 
|  |  | 
|  | size_t PackageParser::parseFDIdentificationArea( | 
|  | DeviceIDRecordCount deviceIdRecCount, const std::vector<uint8_t>& pkgHdr, | 
|  | size_t offset) | 
|  | { | 
|  | size_t pkgHdrRemainingSize = pkgHdr.size() - offset; | 
|  |  | 
|  | while (deviceIdRecCount-- && (pkgHdrRemainingSize > 0)) | 
|  | { | 
|  | pldm_firmware_device_id_record deviceIdRecHeader{}; | 
|  | variable_field applicableComponents{}; | 
|  | variable_field compImageSetVersionStr{}; | 
|  | variable_field recordDescriptors{}; | 
|  | variable_field fwDevicePkgData{}; | 
|  |  | 
|  | auto rc = decode_firmware_device_id_record( | 
|  | pkgHdr.data() + offset, pkgHdrRemainingSize, | 
|  | componentBitmapBitLength, &deviceIdRecHeader, &applicableComponents, | 
|  | &compImageSetVersionStr, &recordDescriptors, &fwDevicePkgData); | 
|  | if (rc) | 
|  | { | 
|  | error( | 
|  | "Failed to decode firmware device ID record, response code '{RC}'", | 
|  | "RC", rc); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | Descriptors descriptors{}; | 
|  | while (deviceIdRecHeader.descriptor_count-- && | 
|  | (recordDescriptors.length > 0)) | 
|  | { | 
|  | uint16_t descriptorType = 0; | 
|  | variable_field descriptorData{}; | 
|  |  | 
|  | rc = decode_descriptor_type_length_value( | 
|  | recordDescriptors.ptr, recordDescriptors.length, | 
|  | &descriptorType, &descriptorData); | 
|  | if (rc) | 
|  | { | 
|  | error( | 
|  | "Failed to decode descriptor type value of type '{TYPE}' and  length '{LENGTH}', response code '{RC}'", | 
|  | "TYPE", descriptorType, "LENGTH", recordDescriptors.length, | 
|  | "RC", rc); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | if (descriptorType != PLDM_FWUP_VENDOR_DEFINED) | 
|  | { | 
|  | descriptors.emplace( | 
|  | descriptorType, | 
|  | DescriptorData{descriptorData.ptr, | 
|  | descriptorData.ptr + descriptorData.length}); | 
|  | } | 
|  | else | 
|  | { | 
|  | uint8_t descTitleStrType = 0; | 
|  | variable_field descTitleStr{}; | 
|  | variable_field vendorDefinedDescData{}; | 
|  |  | 
|  | rc = decode_vendor_defined_descriptor_value( | 
|  | descriptorData.ptr, descriptorData.length, | 
|  | &descTitleStrType, &descTitleStr, &vendorDefinedDescData); | 
|  | if (rc) | 
|  | { | 
|  | error( | 
|  | "Failed to decode vendor-defined descriptor value of type '{TYPE}' and  length '{LENGTH}', response code '{RC}'", | 
|  | "TYPE", descriptorType, "LENGTH", | 
|  | recordDescriptors.length, "RC", rc); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | descriptors.emplace( | 
|  | descriptorType, | 
|  | std::make_tuple(utils::toString(descTitleStr), | 
|  | VendorDefinedDescriptorData{ | 
|  | vendorDefinedDescData.ptr, | 
|  | vendorDefinedDescData.ptr + | 
|  | vendorDefinedDescData.length})); | 
|  | } | 
|  |  | 
|  | auto nextDescriptorOffset = | 
|  | sizeof(pldm_descriptor_tlv().descriptor_type) + | 
|  | sizeof(pldm_descriptor_tlv().descriptor_length) + | 
|  | descriptorData.length; | 
|  | recordDescriptors.ptr += nextDescriptorOffset; | 
|  | recordDescriptors.length -= nextDescriptorOffset; | 
|  | } | 
|  |  | 
|  | DeviceUpdateOptionFlags deviceUpdateOptionFlags = | 
|  | deviceIdRecHeader.device_update_option_flags.value; | 
|  |  | 
|  | ApplicableComponents componentsList; | 
|  |  | 
|  | for (size_t varBitfieldIdx = 0; | 
|  | varBitfieldIdx < applicableComponents.length; varBitfieldIdx++) | 
|  | { | 
|  | std::bitset<8> entry{*(applicableComponents.ptr + varBitfieldIdx)}; | 
|  | for (size_t idx = 0; idx < entry.size(); idx++) | 
|  | { | 
|  | if (entry[idx]) | 
|  | { | 
|  | componentsList.emplace_back( | 
|  | idx + (varBitfieldIdx * entry.size())); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fwDeviceIDRecords.emplace_back(std::make_tuple( | 
|  | deviceUpdateOptionFlags, componentsList, | 
|  | utils::toString(compImageSetVersionStr), std::move(descriptors), | 
|  | FirmwareDevicePackageData{ | 
|  | fwDevicePkgData.ptr, | 
|  | fwDevicePkgData.ptr + fwDevicePkgData.length})); | 
|  | offset += deviceIdRecHeader.record_length; | 
|  | pkgHdrRemainingSize -= deviceIdRecHeader.record_length; | 
|  | } | 
|  |  | 
|  | return offset; | 
|  | } | 
|  |  | 
|  | size_t PackageParser::parseCompImageInfoArea(ComponentImageCount compImageCount, | 
|  | const std::vector<uint8_t>& pkgHdr, | 
|  | size_t offset) | 
|  | { | 
|  | size_t pkgHdrRemainingSize = pkgHdr.size() - offset; | 
|  |  | 
|  | while (compImageCount-- && (pkgHdrRemainingSize > 0)) | 
|  | { | 
|  | pldm_component_image_information compImageInfo{}; | 
|  | variable_field compVersion{}; | 
|  |  | 
|  | auto rc = decode_pldm_comp_image_info( | 
|  | pkgHdr.data() + offset, pkgHdrRemainingSize, &compImageInfo, | 
|  | &compVersion); | 
|  | if (rc) | 
|  | { | 
|  | error( | 
|  | "Failed to decode component image information, response code '{RC}'", | 
|  | "RC", rc); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | CompClassification compClassification = | 
|  | compImageInfo.comp_classification; | 
|  | CompIdentifier compIdentifier = compImageInfo.comp_identifier; | 
|  | CompComparisonStamp compComparisonTime = | 
|  | compImageInfo.comp_comparison_stamp; | 
|  | CompOptions compOptions = compImageInfo.comp_options.value; | 
|  | ReqCompActivationMethod reqCompActivationMethod = | 
|  | compImageInfo.requested_comp_activation_method.value; | 
|  | CompLocationOffset compLocationOffset = | 
|  | compImageInfo.comp_location_offset; | 
|  | CompSize compSize = compImageInfo.comp_size; | 
|  |  | 
|  | componentImageInfos.emplace_back(std::make_tuple( | 
|  | compClassification, compIdentifier, compComparisonTime, compOptions, | 
|  | reqCompActivationMethod, compLocationOffset, compSize, | 
|  | utils::toString(compVersion))); | 
|  | offset += sizeof(pldm_component_image_information) + | 
|  | compImageInfo.comp_version_string_length; | 
|  | pkgHdrRemainingSize -= sizeof(pldm_component_image_information) + | 
|  | compImageInfo.comp_version_string_length; | 
|  | } | 
|  |  | 
|  | return offset; | 
|  | } | 
|  |  | 
|  | void PackageParser::validatePkgTotalSize(uintmax_t pkgSize) | 
|  | { | 
|  | uintmax_t calcPkgSize = pkgHeaderSize; | 
|  | for (const auto& componentImageInfo : componentImageInfos) | 
|  | { | 
|  | CompLocationOffset compLocOffset = std::get<static_cast<size_t>( | 
|  | ComponentImageInfoPos::CompLocationOffsetPos)>(componentImageInfo); | 
|  | CompSize compSize = | 
|  | std::get<static_cast<size_t>(ComponentImageInfoPos::CompSizePos)>( | 
|  | componentImageInfo); | 
|  |  | 
|  | if (compLocOffset != calcPkgSize) | 
|  | { | 
|  | auto cmpVersion = std::get<static_cast<size_t>( | 
|  | ComponentImageInfoPos::CompVersionPos)>(componentImageInfo); | 
|  | error( | 
|  | "Failed to validate the component location offset '{OFFSET}' for version '{COMPONENT_VERSION}' and package size '{SIZE}'", | 
|  | "OFFSET", compLocOffset, "COMPONENT_VERSION", cmpVersion, | 
|  | "SIZE", calcPkgSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | calcPkgSize += compSize; | 
|  | } | 
|  |  | 
|  | if (calcPkgSize != pkgSize) | 
|  | { | 
|  | error( | 
|  | "Failed to match package size '{PKG_SIZE}' to calculated package size '{CALCULATED_PACKAGE_SIZE}'.", | 
|  | "PKG_SIZE", pkgSize, "CALCULATED_PACKAGE_SIZE", calcPkgSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PackageParserV1::parse(const std::vector<uint8_t>& pkgHdr, | 
|  | uintmax_t pkgSize) | 
|  | { | 
|  | if (pkgHeaderSize >= pkgHdr.size()) | 
|  | { | 
|  | error("Invalid package header size '{PKG_HDR_SIZE}' ", "PKG_HDR_SIZE", | 
|  | pkgHeaderSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | size_t offset = sizeof(pldm_package_header_information) + pkgVersion.size(); | 
|  | if (offset + sizeof(DeviceIDRecordCount) >= pkgHeaderSize) | 
|  | { | 
|  | error("Failed to parse package header of size '{PKG_HDR_SIZE}'", | 
|  | "PKG_HDR_SIZE", pkgHeaderSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | auto deviceIdRecCount = static_cast<DeviceIDRecordCount>(pkgHdr[offset]); | 
|  | offset += sizeof(DeviceIDRecordCount); | 
|  |  | 
|  | offset = parseFDIdentificationArea(deviceIdRecCount, pkgHdr, offset); | 
|  | if (deviceIdRecCount != fwDeviceIDRecords.size()) | 
|  | { | 
|  | error("Failed to find DeviceIDRecordCount {DREC_CNT} entries", | 
|  | "DREC_CNT", deviceIdRecCount); | 
|  | throw InternalFailure(); | 
|  | } | 
|  | if (offset + sizeof(ComponentImageCount) >= pkgHeaderSize) | 
|  | { | 
|  | error("Failed to parsing package header of size '{PKG_HDR_SIZE}'", | 
|  | "PKG_HDR_SIZE", pkgHeaderSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | auto compImageCount = static_cast<ComponentImageCount>( | 
|  | le16toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8))); | 
|  | offset += sizeof(ComponentImageCount); | 
|  |  | 
|  | offset = parseCompImageInfoArea(compImageCount, pkgHdr, offset); | 
|  | if (compImageCount != componentImageInfos.size()) | 
|  | { | 
|  | error("Failed to find ComponentImageCount '{COMP_IMG_CNT}' entries", | 
|  | "COMP_IMG_CNT", compImageCount); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | if (offset + sizeof(PackageHeaderChecksum) != pkgHeaderSize) | 
|  | { | 
|  | error("Failed to parse package header of size '{PKG_HDR_SIZE}'", | 
|  | "PKG_HDR_SIZE", pkgHeaderSize); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | auto calcChecksum = pldm_edac_crc32(pkgHdr.data(), offset); | 
|  | auto checksum = static_cast<PackageHeaderChecksum>( | 
|  | le32toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8) | | 
|  | (pkgHdr[offset + 2] << 16) | (pkgHdr[offset + 3] << 24))); | 
|  | if (calcChecksum != checksum) | 
|  | { | 
|  | error( | 
|  | "Failed to parse package header for calculated checksum '{CALCULATED_CHECKSUM}' and header checksum '{PACKAGE_HEADER_CHECKSUM}'", | 
|  | "CALCULATED_CHECKSUM", calcChecksum, "PACKAGE_HEADER_CHECKSUM", | 
|  | checksum); | 
|  | throw InternalFailure(); | 
|  | } | 
|  |  | 
|  | validatePkgTotalSize(pkgSize); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<PackageParser> parsePackageHeader(std::vector<uint8_t>& pkgData) | 
|  | { | 
|  | constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH> hdrIdentifierv1{ | 
|  | 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, | 
|  | 0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02}; | 
|  | constexpr uint8_t pkgHdrVersion1 = 0x01; | 
|  |  | 
|  | pldm_package_header_information pkgHeader{}; | 
|  | variable_field pkgVersion{}; | 
|  | auto rc = decode_pldm_package_header_info(pkgData.data(), pkgData.size(), | 
|  | &pkgHeader, &pkgVersion); | 
|  | if (rc) | 
|  | { | 
|  | error( | 
|  | "Failed to decode PLDM package header information, response code '{RC}'", | 
|  | "RC", rc); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (std::equal(pkgHeader.uuid, pkgHeader.uuid + PLDM_FWUP_UUID_LENGTH, | 
|  | hdrIdentifierv1.begin(), hdrIdentifierv1.end()) && | 
|  | (pkgHeader.package_header_format_version == pkgHdrVersion1)) | 
|  | { | 
|  | PackageHeaderSize pkgHdrSize = pkgHeader.package_header_size; | 
|  | ComponentBitmapBitLength componentBitmapBitLength = | 
|  | pkgHeader.component_bitmap_bit_length; | 
|  | return std::make_unique<PackageParserV1>( | 
|  | pkgHdrSize, utils::toString(pkgVersion), componentBitmapBitLength); | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | } // namespace fw_update | 
|  |  | 
|  | } // namespace pldm |