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