1 #include "pldm_package_util.hpp" 2 3 #include "package_parser.hpp" 4 #include "types.hpp" 5 6 #include <fcntl.h> 7 #include <libpldm/base.h> 8 #include <libpldm/firmware_update.h> 9 #include <libpldm/pldm_types.h> 10 #include <libpldm/utils.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <sys/mman.h> 14 15 #include <phosphor-logging/lg2.hpp> 16 #include <sdbusplus/message/native_types.hpp> 17 18 #include <cassert> 19 #include <cstring> 20 21 using namespace pldm::fw_update; 22 23 namespace pldm_package_util 24 { 25 26 std::shared_ptr<PackageParser> parsePLDMPackage(const uint8_t* buf, size_t size) 27 { 28 std::vector<uint8_t> pkgData; 29 30 pkgData.reserve(size); 31 for (size_t i = 0; i < size; i++) 32 { 33 pkgData.push_back(buf[i]); 34 } 35 36 lg2::debug("parsing package header"); 37 38 std::unique_ptr<PackageParser> packageParser = 39 pldm::fw_update::parsePackageHeader(pkgData); 40 41 if (packageParser == nullptr) 42 { 43 lg2::error("could not parse package header"); 44 return packageParser; 45 } 46 47 lg2::debug("parsing package, pkg header size: {N}", "N", 48 packageParser->pkgHeaderSize); 49 50 std::vector<uint8_t> pkgHeaderOnly; 51 pkgHeaderOnly.insert( 52 pkgHeaderOnly.end(), pkgData.begin(), 53 pkgData.begin() + (long)(packageParser->pkgHeaderSize)); 54 55 packageParser->parse(pkgHeaderOnly, pkgData.size()); 56 57 return packageParser; 58 } 59 60 int readImagePackage(FILE* file, uint8_t* package_data, 61 const size_t package_size) 62 { 63 if (file == NULL || package_data == NULL) 64 { 65 return 1; 66 } 67 68 if (package_size == 0) 69 { 70 lg2::error("Package size is 0"); 71 return 1; 72 } 73 74 lg2::debug("reading {NBYTES} bytes from file", "NBYTES", package_size); 75 76 // Read the package into memory 77 if (fread(package_data, 1, package_size, file) != package_size) 78 { 79 perror("Failed to read package data"); 80 fclose(file); 81 return 1; 82 } 83 84 return 0; 85 } 86 87 void* mmapImagePackage(sdbusplus::message::unix_fd image, size_t* size_out) 88 { 89 lg2::debug("open fd {FD}", "FD", int(image)); 90 91 off_t size = lseek(image.fd, 0, SEEK_END); 92 93 if (size < 0) 94 { 95 lg2::error("failed to determine file size"); 96 perror("error:"); 97 return NULL; 98 } 99 100 *size_out = size; 101 102 lg2::debug("file size: {SIZE}", "SIZE", (uint64_t)size); 103 104 void* data = mmap(nullptr, size, PROT_READ, MAP_SHARED, image.fd, 0); 105 106 if (data == MAP_FAILED) 107 { 108 lg2::error("could not mmap the image"); 109 return NULL; 110 } 111 112 return data; 113 } 114 115 bool fwDeviceIDRecordMatchesCompatible(const FirmwareDeviceIDRecord& record, 116 const std::string& compatible) 117 { 118 Descriptors desc = std::get<3>(record); 119 if (desc.empty()) 120 { 121 return false; 122 } 123 124 if (!desc.contains(0xffff)) 125 { 126 return false; 127 } 128 129 auto v = desc[0xffff]; 130 131 if (!std::holds_alternative<VendorDefinedDescriptorInfo>(v)) 132 { 133 lg2::debug( 134 "descriptor does not have the vendor defined descriptor info"); 135 return false; 136 } 137 138 auto data = std::get<VendorDefinedDescriptorInfo>(v); 139 140 std::string actualCompatible = std::get<VendorDefinedDescriptorTitle>(data); 141 142 return compatible == actualCompatible; 143 } 144 145 bool fwDeviceIDRecordMatchesIANA(const FirmwareDeviceIDRecord& record, 146 uint32_t vendorIANA) 147 { 148 Descriptors desc = std::get<3>(record); 149 150 if (desc.empty()) 151 { 152 return false; 153 } 154 155 if (!desc.contains(0x1)) 156 { 157 lg2::error("did not find iana enterprise id"); 158 return false; 159 } 160 161 auto viana = desc[0x1]; 162 163 if (!std::holds_alternative<DescriptorData>(viana)) 164 { 165 lg2::error("did not find iana enterprise id"); 166 return false; 167 } 168 169 const DescriptorData& dd = std::get<DescriptorData>(viana); 170 171 if (dd.size() != 4) 172 { 173 lg2::error("descriptor data wrong size ( != 4) for vendor iana"); 174 return false; 175 } 176 177 const uint32_t actualIANA = dd[0] | dd[1] << 8 | dd[2] << 16 | dd[3] << 24; 178 179 return actualIANA == vendorIANA; 180 } 181 182 bool fwDeviceIDRecordMatches(const FirmwareDeviceIDRecord& record, 183 uint32_t vendorIANA, const std::string& compatible) 184 { 185 return fwDeviceIDRecordMatchesIANA(record, vendorIANA) && 186 fwDeviceIDRecordMatchesCompatible(record, compatible); 187 } 188 189 ssize_t findMatchingDeviceDescriptorIndex( 190 const FirmwareDeviceIDRecords& records, uint32_t vendorIANA, 191 const std::string& compatible) 192 { 193 for (size_t i = 0; i < records.size(); i++) 194 { 195 const FirmwareDeviceIDRecord& record = records[i]; 196 if (fwDeviceIDRecordMatches(record, vendorIANA, compatible)) 197 { 198 return (ssize_t)i; 199 } 200 } 201 return -1; 202 } 203 204 int extractMatchingComponentImage( 205 const std::shared_ptr<PackageParser>& packageParser, 206 const std::string& compatible, uint32_t vendorIANA, 207 uint32_t* component_offset_out, size_t* component_size_out) 208 { 209 const FirmwareDeviceIDRecords& fwDeviceIdRecords = 210 packageParser->getFwDeviceIDRecords(); 211 212 // find fw descriptor matching vendor iana and compatible 213 ssize_t deviceDescriptorIndex = findMatchingDeviceDescriptorIndex( 214 fwDeviceIdRecords, vendorIANA, compatible); 215 216 if (deviceDescriptorIndex < 0) 217 { 218 lg2::error( 219 "did not find a matching device descriptor for {IANA}, {COMPATIBLE}", 220 "IANA", lg2::hex, vendorIANA, "COMPATIBLE", compatible); 221 return EXIT_FAILURE; 222 } 223 224 const FirmwareDeviceIDRecord& descriptor = 225 fwDeviceIdRecords[deviceDescriptorIndex]; 226 // find applicable components 227 // iterate over components to find the applicable component 228 229 ApplicableComponents ac = std::get<1>(descriptor); 230 231 if (ac.empty()) 232 { 233 lg2::error("did not find an applicable component image for the device"); 234 return EXIT_FAILURE; 235 } 236 237 // component is 0 based index 238 const size_t component = ac[0]; 239 240 const ComponentImageInfos& cs = packageParser->getComponentImageInfos(); 241 242 if (component >= cs.size()) 243 { 244 lg2::error("applicable component out of bounds"); 245 return EXIT_FAILURE; 246 } 247 248 const ComponentImageInfo& c = cs[component]; 249 250 CompLocationOffset off = std::get<5>(c); 251 CompSize size = std::get<6>(c); 252 253 *component_offset_out = off; 254 *component_size_out = size; 255 256 return EXIT_SUCCESS; 257 } 258 259 } // namespace pldm_package_util 260