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