blob: c3e92e51a9538fec8e384c392b4ee4d5f4f9ec73 [file] [log] [blame]
Alexander Hansen00b3cae2024-11-26 17:32:33 +01001#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
21using namespace pldm::fw_update;
22
23namespace pldm_package_util
24{
25
26std::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
60int 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
87void* 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
115bool 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
145bool 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
182bool fwDeviceIDRecordMatches(const FirmwareDeviceIDRecord& record,
183 uint32_t vendorIANA, const std::string& compatible)
184{
185 return fwDeviceIDRecordMatchesIANA(record, vendorIANA) &&
186 fwDeviceIDRecordMatchesCompatible(record, compatible);
187}
188
189ssize_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
204int 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