xref: /openbmc/phosphor-bmc-code-mgmt/common/pldm/pldm_package_util.cpp (revision 7dfe0725740b129a3d5803f0ceaaea5ac0102a28)
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