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