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