blob: 2d348a021c5e08b06a256e8bce3ff6ed02ca664a [file] [log] [blame]
#include "pldm_package_util.hpp"
#include "package_parser.hpp"
#include "types.hpp"
#include <fcntl.h>
#include <libpldm/base.h>
#include <libpldm/firmware_update.h>
#include <libpldm/pldm_types.h>
#include <libpldm/utils.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <cassert>
#include <cstring>
#include <functional>
PHOSPHOR_LOG2_USING;
using namespace pldm::fw_update;
namespace pldm_package_util
{
std::shared_ptr<PackageParser> parsePLDMPackage(const uint8_t* buf, size_t size)
{
std::vector<uint8_t> pkgData;
pkgData.reserve(size);
for (size_t i = 0; i < size; i++)
{
pkgData.push_back(buf[i]);
}
debug("parsing package header");
std::unique_ptr<PackageParser> packageParser =
pldm::fw_update::parsePackageHeader(pkgData);
if (packageParser == nullptr)
{
error("could not parse package header");
return packageParser;
}
debug("parsing package, pkg header size: {N}", "N",
packageParser->pkgHeaderSize);
std::vector<uint8_t> pkgHeaderOnly;
pkgHeaderOnly.insert(
pkgHeaderOnly.end(), pkgData.begin(),
pkgData.begin() + (long)(packageParser->pkgHeaderSize));
packageParser->parse(pkgHeaderOnly, pkgData.size());
return packageParser;
}
int readImagePackage(FILE* file, uint8_t* packageData, const size_t packageSize)
{
if (file == NULL || packageData == NULL)
{
return 1;
}
if (packageSize == 0)
{
error("Package size is 0");
return 1;
}
debug("reading {NBYTES} bytes from file", "NBYTES", packageSize);
// Read the package into memory
if (fread(packageData, 1, packageSize, file) != packageSize)
{
perror("Failed to read package data");
fclose(file);
return 1;
}
return 0;
}
std::unique_ptr<void, std::function<void(void*)>> mmapImagePackage(
sdbusplus::message::unix_fd image, size_t* sizeOut)
{
debug("open fd {FD}", "FD", int(image));
off_t size = lseek(image.fd, 0, SEEK_END);
if (size < 0)
{
error("failed to determine file size");
perror("error:");
return nullptr;
}
*sizeOut = size;
debug("file size: {SIZE}", "SIZE", (uint64_t)size);
void* data = mmap(nullptr, size, PROT_READ, MAP_SHARED, image.fd, 0);
if (data == MAP_FAILED)
{
error("could not mmap the image");
return nullptr;
}
using mmapUniquePtr = std::unique_ptr<void, std::function<void(void*)>>;
mmapUniquePtr dataUnique(data, [size](void* arg) {
if (munmap(arg, size) != 0)
{
error("Failed to un map the PLDM package");
}
});
return dataUnique;
}
bool fwDeviceIDRecordMatchesCompatible(const FirmwareDeviceIDRecord& record,
const std::string& compatible)
{
Descriptors desc = std::get<3>(record);
if (desc.empty())
{
return false;
}
if (!desc.contains(0xffff))
{
return false;
}
auto v = desc[0xffff];
if (!std::holds_alternative<VendorDefinedDescriptorInfo>(v))
{
debug("descriptor does not have the vendor defined descriptor info");
return false;
}
auto data = std::get<VendorDefinedDescriptorInfo>(v);
std::string actualCompatible = std::get<VendorDefinedDescriptorTitle>(data);
return compatible == actualCompatible;
}
bool fwDeviceIDRecordMatchesIANA(const FirmwareDeviceIDRecord& record,
uint32_t vendorIANA)
{
Descriptors desc = std::get<3>(record);
if (desc.empty())
{
return false;
}
if (!desc.contains(0x1))
{
error("did not find iana enterprise id");
return false;
}
auto viana = desc[0x1];
if (!std::holds_alternative<DescriptorData>(viana))
{
error("did not find iana enterprise id");
return false;
}
const DescriptorData& dd = std::get<DescriptorData>(viana);
if (dd.size() != 4)
{
error("descriptor data wrong size ( != 4) for vendor iana");
return false;
}
const uint32_t actualIANA = dd[0] | dd[1] << 8 | dd[2] << 16 | dd[3] << 24;
return actualIANA == vendorIANA;
}
bool fwDeviceIDRecordMatches(const FirmwareDeviceIDRecord& record,
uint32_t vendorIANA, const std::string& compatible)
{
return fwDeviceIDRecordMatchesIANA(record, vendorIANA) &&
fwDeviceIDRecordMatchesCompatible(record, compatible);
}
ssize_t findMatchingDeviceDescriptorIndex(
const FirmwareDeviceIDRecords& records, uint32_t vendorIANA,
const std::string& compatible)
{
for (size_t i = 0; i < records.size(); i++)
{
const FirmwareDeviceIDRecord& record = records[i];
if (fwDeviceIDRecordMatches(record, vendorIANA, compatible))
{
return (ssize_t)i;
}
}
return -1;
}
int extractMatchingComponentImage(
const std::shared_ptr<PackageParser>& packageParser,
const std::string& compatible, uint32_t vendorIANA,
uint32_t* componentOffsetOut, size_t* componentSizeOut,
std::string& componentVersionOut)
{
const FirmwareDeviceIDRecords& fwDeviceIdRecords =
packageParser->getFwDeviceIDRecords();
// find fw descriptor matching vendor iana and compatible
ssize_t deviceDescriptorIndex = findMatchingDeviceDescriptorIndex(
fwDeviceIdRecords, vendorIANA, compatible);
if (deviceDescriptorIndex < 0)
{
error(
"did not find a matching device descriptor for {IANA}, {COMPATIBLE}",
"IANA", lg2::hex, vendorIANA, "COMPATIBLE", compatible);
return EXIT_FAILURE;
}
const FirmwareDeviceIDRecord& descriptor =
fwDeviceIdRecords[deviceDescriptorIndex];
// find applicable components
// iterate over components to find the applicable component
ApplicableComponents ac = std::get<1>(descriptor);
if (ac.empty())
{
error("did not find an applicable component image for the device");
return EXIT_FAILURE;
}
// component is 0 based index
const size_t component = ac[0];
const ComponentImageInfos& cs = packageParser->getComponentImageInfos();
if (component >= cs.size())
{
error("applicable component out of bounds");
return EXIT_FAILURE;
}
const ComponentImageInfo& c = cs[component];
CompLocationOffset off = std::get<5>(c);
CompSize size = std::get<6>(c);
*componentOffsetOut = off;
*componentSizeOut = size;
componentVersionOut = std::get<7>(c);
return EXIT_SUCCESS;
}
} // namespace pldm_package_util