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