xref: /openbmc/phosphor-bmc-code-mgmt/common/pldm/package_parser.cpp (revision ec807fcb376e54e5526a9c47fa702c1642d07f90)
1 #include "package_parser.hpp"
2 
3 #include <libpldm/firmware_update.h>
4 #include <libpldm/utils.h>
5 
6 #include <phosphor-logging/lg2.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 
9 #include <memory>
10 
11 // NOLINTBEGIN
12 
13 using namespace std;
14 
15 PHOSPHOR_LOG2_USING;
16 
17 namespace pldm
18 {
19 
20 namespace utils
21 {
22 
23 std::string toString(const struct variable_field& var)
24 {
25     if (var.ptr == nullptr || !var.length)
26     {
27         return "";
28     }
29 
30     std::string str(reinterpret_cast<const char*>(var.ptr), var.length);
31     std::replace_if(
32         str.begin(), str.end(), [](const char& c) { return !isprint(c); }, ' ');
33     return str;
34 }
35 
36 } // namespace utils
37 
38 namespace fw_update
39 {
40 
41 using InternalFailure =
42     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
43 
44 size_t PackageParser::parseFDIdentificationArea(
45     DeviceIDRecordCount deviceIdRecCount, const std::vector<uint8_t>& pkgHdr,
46     size_t offset)
47 {
48     size_t pkgHdrRemainingSize = pkgHdr.size() - offset;
49 
50     while (deviceIdRecCount-- && (pkgHdrRemainingSize > 0))
51     {
52         pldm_firmware_device_id_record deviceIdRecHeader{};
53         variable_field applicableComponents{};
54         variable_field compImageSetVersionStr{};
55         variable_field recordDescriptors{};
56         variable_field fwDevicePkgData{};
57 
58         auto rc = decode_firmware_device_id_record(
59             pkgHdr.data() + offset, pkgHdrRemainingSize,
60             componentBitmapBitLength, &deviceIdRecHeader, &applicableComponents,
61             &compImageSetVersionStr, &recordDescriptors, &fwDevicePkgData);
62         if (rc)
63         {
64             error(
65                 "Failed to decode firmware device ID record, response code '{RC}'",
66                 "RC", rc);
67             throw InternalFailure();
68         }
69 
70         Descriptors descriptors{};
71         while (deviceIdRecHeader.descriptor_count-- &&
72                (recordDescriptors.length > 0))
73         {
74             uint16_t descriptorType = 0;
75             variable_field descriptorData{};
76 
77             rc = decode_descriptor_type_length_value(
78                 recordDescriptors.ptr, recordDescriptors.length,
79                 &descriptorType, &descriptorData);
80             if (rc)
81             {
82                 error(
83                     "Failed to decode descriptor type value of type '{TYPE}' and  length '{LENGTH}', response code '{RC}'",
84                     "TYPE", descriptorType, "LENGTH", recordDescriptors.length,
85                     "RC", rc);
86                 throw InternalFailure();
87             }
88 
89             if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
90             {
91                 descriptors.emplace(
92                     descriptorType,
93                     DescriptorData{descriptorData.ptr,
94                                    descriptorData.ptr + descriptorData.length});
95             }
96             else
97             {
98                 uint8_t descTitleStrType = 0;
99                 variable_field descTitleStr{};
100                 variable_field vendorDefinedDescData{};
101 
102                 rc = decode_vendor_defined_descriptor_value(
103                     descriptorData.ptr, descriptorData.length,
104                     &descTitleStrType, &descTitleStr, &vendorDefinedDescData);
105                 if (rc)
106                 {
107                     error(
108                         "Failed to decode vendor-defined descriptor value of type '{TYPE}' and  length '{LENGTH}', response code '{RC}'",
109                         "TYPE", descriptorType, "LENGTH",
110                         recordDescriptors.length, "RC", rc);
111                     throw InternalFailure();
112                 }
113 
114                 descriptors.emplace(
115                     descriptorType,
116                     std::make_tuple(utils::toString(descTitleStr),
117                                     VendorDefinedDescriptorData{
118                                         vendorDefinedDescData.ptr,
119                                         vendorDefinedDescData.ptr +
120                                             vendorDefinedDescData.length}));
121             }
122 
123             auto nextDescriptorOffset =
124                 sizeof(pldm_descriptor_tlv().descriptor_type) +
125                 sizeof(pldm_descriptor_tlv().descriptor_length) +
126                 descriptorData.length;
127             recordDescriptors.ptr += nextDescriptorOffset;
128             recordDescriptors.length -= nextDescriptorOffset;
129         }
130 
131         DeviceUpdateOptionFlags deviceUpdateOptionFlags =
132             deviceIdRecHeader.device_update_option_flags.value;
133 
134         ApplicableComponents componentsList;
135 
136         for (size_t varBitfieldIdx = 0;
137              varBitfieldIdx < applicableComponents.length; varBitfieldIdx++)
138         {
139             std::bitset<8> entry{*(applicableComponents.ptr + varBitfieldIdx)};
140             for (size_t idx = 0; idx < entry.size(); idx++)
141             {
142                 if (entry[idx])
143                 {
144                     componentsList.emplace_back(
145                         idx + (varBitfieldIdx * entry.size()));
146                 }
147             }
148         }
149 
150         fwDeviceIDRecords.emplace_back(std::make_tuple(
151             deviceUpdateOptionFlags, componentsList,
152             utils::toString(compImageSetVersionStr), std::move(descriptors),
153             FirmwareDevicePackageData{
154                 fwDevicePkgData.ptr,
155                 fwDevicePkgData.ptr + fwDevicePkgData.length}));
156         offset += deviceIdRecHeader.record_length;
157         pkgHdrRemainingSize -= deviceIdRecHeader.record_length;
158     }
159 
160     return offset;
161 }
162 
163 size_t PackageParser::parseCompImageInfoArea(ComponentImageCount compImageCount,
164                                              const std::vector<uint8_t>& pkgHdr,
165                                              size_t offset)
166 {
167     size_t pkgHdrRemainingSize = pkgHdr.size() - offset;
168 
169     while (compImageCount-- && (pkgHdrRemainingSize > 0))
170     {
171         pldm_component_image_information compImageInfo{};
172         variable_field compVersion{};
173 
174         auto rc = decode_pldm_comp_image_info(
175             pkgHdr.data() + offset, pkgHdrRemainingSize, &compImageInfo,
176             &compVersion);
177         if (rc)
178         {
179             error(
180                 "Failed to decode component image information, response code '{RC}'",
181                 "RC", rc);
182             throw InternalFailure();
183         }
184 
185         CompClassification compClassification =
186             compImageInfo.comp_classification;
187         CompIdentifier compIdentifier = compImageInfo.comp_identifier;
188         CompComparisonStamp compComparisonTime =
189             compImageInfo.comp_comparison_stamp;
190         CompOptions compOptions = compImageInfo.comp_options.value;
191         ReqCompActivationMethod reqCompActivationMethod =
192             compImageInfo.requested_comp_activation_method.value;
193         CompLocationOffset compLocationOffset =
194             compImageInfo.comp_location_offset;
195         CompSize compSize = compImageInfo.comp_size;
196 
197         componentImageInfos.emplace_back(std::make_tuple(
198             compClassification, compIdentifier, compComparisonTime, compOptions,
199             reqCompActivationMethod, compLocationOffset, compSize,
200             utils::toString(compVersion)));
201         offset += sizeof(pldm_component_image_information) +
202                   compImageInfo.comp_version_string_length;
203         pkgHdrRemainingSize -= sizeof(pldm_component_image_information) +
204                                compImageInfo.comp_version_string_length;
205     }
206 
207     return offset;
208 }
209 
210 void PackageParser::validatePkgTotalSize(uintmax_t pkgSize)
211 {
212     uintmax_t calcPkgSize = pkgHeaderSize;
213     for (const auto& componentImageInfo : componentImageInfos)
214     {
215         CompLocationOffset compLocOffset = std::get<static_cast<size_t>(
216             ComponentImageInfoPos::CompLocationOffsetPos)>(componentImageInfo);
217         CompSize compSize =
218             std::get<static_cast<size_t>(ComponentImageInfoPos::CompSizePos)>(
219                 componentImageInfo);
220 
221         if (compLocOffset != calcPkgSize)
222         {
223             auto cmpVersion = std::get<static_cast<size_t>(
224                 ComponentImageInfoPos::CompVersionPos)>(componentImageInfo);
225             error(
226                 "Failed to validate the component location offset '{OFFSET}' for version '{COMPONENT_VERSION}' and package size '{SIZE}'",
227                 "OFFSET", compLocOffset, "COMPONENT_VERSION", cmpVersion,
228                 "SIZE", calcPkgSize);
229             throw InternalFailure();
230         }
231 
232         calcPkgSize += compSize;
233     }
234 
235     if (calcPkgSize != pkgSize)
236     {
237         error(
238             "Failed to match package size '{PKG_SIZE}' to calculated package size '{CALCULATED_PACKAGE_SIZE}'.",
239             "PKG_SIZE", pkgSize, "CALCULATED_PACKAGE_SIZE", calcPkgSize);
240         throw InternalFailure();
241     }
242 }
243 
244 void PackageParserV1::parse(const std::vector<uint8_t>& pkgHdr,
245                             uintmax_t pkgSize)
246 {
247     if (pkgHeaderSize != pkgHdr.size())
248     {
249         error("Invalid package header size '{PKG_HDR_SIZE}' ", "PKG_HDR_SIZE",
250               pkgHeaderSize);
251         throw InternalFailure();
252     }
253 
254     size_t offset = sizeof(pldm_package_header_information) + pkgVersion.size();
255     if (offset + sizeof(DeviceIDRecordCount) >= pkgHeaderSize)
256     {
257         error("Failed to parse package header of size '{PKG_HDR_SIZE}'",
258               "PKG_HDR_SIZE", pkgHeaderSize);
259         throw InternalFailure();
260     }
261 
262     auto deviceIdRecCount = static_cast<DeviceIDRecordCount>(pkgHdr[offset]);
263     offset += sizeof(DeviceIDRecordCount);
264 
265     offset = parseFDIdentificationArea(deviceIdRecCount, pkgHdr, offset);
266     if (deviceIdRecCount != fwDeviceIDRecords.size())
267     {
268         error("Failed to find DeviceIDRecordCount {DREC_CNT} entries",
269               "DREC_CNT", deviceIdRecCount);
270         throw InternalFailure();
271     }
272     if (offset + sizeof(ComponentImageCount) >= pkgHeaderSize)
273     {
274         error("Failed to parsing package header of size '{PKG_HDR_SIZE}'",
275               "PKG_HDR_SIZE", pkgHeaderSize);
276         throw InternalFailure();
277     }
278 
279     auto compImageCount = static_cast<ComponentImageCount>(
280         le16toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8)));
281     offset += sizeof(ComponentImageCount);
282 
283     offset = parseCompImageInfoArea(compImageCount, pkgHdr, offset);
284     if (compImageCount != componentImageInfos.size())
285     {
286         error("Failed to find ComponentImageCount '{COMP_IMG_CNT}' entries",
287               "COMP_IMG_CNT", compImageCount);
288         throw InternalFailure();
289     }
290 
291     if (offset + sizeof(PackageHeaderChecksum) != pkgHeaderSize)
292     {
293         error("Failed to parse package header of size '{PKG_HDR_SIZE}'",
294               "PKG_HDR_SIZE", pkgHeaderSize);
295         throw InternalFailure();
296     }
297 
298     auto calcChecksum = crc32(pkgHdr.data(), offset);
299     auto checksum = static_cast<PackageHeaderChecksum>(
300         le32toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8) |
301                 (pkgHdr[offset + 2] << 16) | (pkgHdr[offset + 3] << 24)));
302     if (calcChecksum != checksum)
303     {
304         error(
305             "Failed to parse package header for calculated checksum '{CALCULATED_CHECKSUM}' and header checksum '{PACKAGE_HEADER_CHECKSUM}'",
306             "CALCULATED_CHECKSUM", calcChecksum, "PACKAGE_HEADER_CHECKSUM",
307             checksum);
308         throw InternalFailure();
309     }
310 
311     validatePkgTotalSize(pkgSize);
312 }
313 
314 std::unique_ptr<PackageParser> parsePackageHeader(std::vector<uint8_t>& pkgData)
315 {
316     constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH> hdrIdentifierv1{
317         0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43,
318         0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02};
319     constexpr uint8_t pkgHdrVersion1 = 0x01;
320 
321     pldm_package_header_information pkgHeader{};
322     variable_field pkgVersion{};
323     auto rc = decode_pldm_package_header_info(pkgData.data(), pkgData.size(),
324                                               &pkgHeader, &pkgVersion);
325     if (rc)
326     {
327         error(
328             "Failed to decode PLDM package header information, response code '{RC}'",
329             "RC", rc);
330         return nullptr;
331     }
332 
333     if (std::equal(pkgHeader.uuid, pkgHeader.uuid + PLDM_FWUP_UUID_LENGTH,
334                    hdrIdentifierv1.begin(), hdrIdentifierv1.end()) &&
335         (pkgHeader.package_header_format_version == pkgHdrVersion1))
336     {
337         PackageHeaderSize pkgHdrSize = pkgHeader.package_header_size;
338         ComponentBitmapBitLength componentBitmapBitLength =
339             pkgHeader.component_bitmap_bit_length;
340         return std::make_unique<PackageParserV1>(
341             pkgHdrSize, utils::toString(pkgVersion), componentBitmapBitLength);
342     }
343 
344     return nullptr;
345 }
346 
347 } // namespace fw_update
348 
349 } // namespace pldm
350 
351 // NOLINTEND
352