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