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