xref: /openbmc/bios-bmc-smm-error-logger/src/rde/rde_handler.cpp (revision e710a3677b03caf3be6a8c4c54c35652524f719e)
1 #include "rde/rde_handler.hpp"
2 
3 #include <stdplus/print.hpp>
4 
5 #include <format>
6 #include <iostream>
7 
8 namespace bios_bmc_smm_error_logger
9 {
10 namespace rde
11 {
12 
13 /**
14  * @brief CRC-32 divisor.
15  *
16  * This is equivalent to the one used by IEEE802.3.
17  */
18 constexpr uint32_t crcDevisor = 0xedb88320;
19 
RdeCommandHandler(std::unique_ptr<ExternalStorerInterface> exStorer)20 RdeCommandHandler::RdeCommandHandler(
21     std::unique_ptr<ExternalStorerInterface> exStorer) :
22     flagState(RdeDictTransferFlagState::RdeStateIdle),
23     exStorer(std::move(exStorer)), prevDictResourceId(0), crc(0xFFFFFFFF)
24 {
25     // Initialize CRC table.
26     calcCrcTable();
27 }
28 
decodeRdeCommand(std::span<const uint8_t> rdeCommand,RdeCommandType type)29 RdeDecodeStatus RdeCommandHandler::decodeRdeCommand(
30     std::span<const uint8_t> rdeCommand, RdeCommandType type)
31 {
32     if (type == RdeCommandType::RdeMultiPartReceiveResponse)
33     {
34         return multiPartReceiveResp(rdeCommand);
35     }
36     if (type == RdeCommandType::RdeOperationInitRequest)
37     {
38         return operationInitRequest(rdeCommand);
39     }
40 
41     stdplus::print(stderr, "Invalid command type\n");
42     return RdeDecodeStatus::RdeInvalidCommand;
43 }
44 
getDictionaryCount()45 uint32_t RdeCommandHandler::getDictionaryCount()
46 {
47     return dictionaryManager.getDictionaryCount();
48 }
49 
operationInitRequest(std::span<const uint8_t> rdeCommand)50 RdeDecodeStatus RdeCommandHandler::operationInitRequest(
51     std::span<const uint8_t> rdeCommand)
52 {
53     // Ensure rdeCommand is large enough for the header.
54     if (rdeCommand.size() < sizeof(RdeOperationInitReqHeader))
55     {
56         stdplus::print(
57             stderr,
58             "RDE OperationInitRequest command is smaller than the expected header size. Received: {}, Expected: {}\n",
59             rdeCommand.size(), sizeof(RdeOperationInitReqHeader));
60         return RdeDecodeStatus::RdeInvalidCommand;
61     }
62 
63     const RdeOperationInitReqHeader* header =
64         reinterpret_cast<const RdeOperationInitReqHeader*>(rdeCommand.data());
65 
66     // Check if there is a payload. If not, we are not doing anything.
67     if (!header->containsRequestPayload)
68     {
69         return RdeDecodeStatus::RdeOk;
70     }
71 
72     // Ensure rdeCommand is large enough for header + locator + declared
73     // payload.
74     size_t expectedTotalSize =
75         sizeof(RdeOperationInitReqHeader) + header->operationLocatorLength +
76         header->requestPayloadLength;
77     if (rdeCommand.size() < expectedTotalSize)
78     {
79         stdplus::print(
80             stderr,
81             "RDE OperationInitRequest command size is smaller than header + locator + declared payload size. Received: {}, Expected: {}\n",
82             rdeCommand.size(), expectedTotalSize);
83         return RdeDecodeStatus::RdeInvalidCommand;
84     }
85 
86     if (header->operationType !=
87         static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate))
88     {
89         stdplus::print(stderr, "Operation not supported\n");
90         return RdeDecodeStatus::RdeUnsupportedOperation;
91     }
92 
93     // OperationInit payload overflows are not suported.
94     if (header->sendDataTransferHandle != 0)
95     {
96         stdplus::print(stderr, "Payload should fit in within the request\n");
97         return RdeDecodeStatus::RdePayloadOverflow;
98     }
99 
100     auto schemaDictOrErr = dictionaryManager.getDictionary(header->resourceID);
101     if (!schemaDictOrErr)
102     {
103         stdplus::print(stderr,
104                        "Schema Dictionary not found for resourceId: {}\n",
105                        header->resourceID);
106         return RdeDecodeStatus::RdeNoDictionary;
107     }
108 
109     auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary();
110     if (!annotationDictOrErr)
111     {
112         stdplus::print(stderr, "Annotation dictionary not found\n");
113         return RdeDecodeStatus::RdeNoDictionary;
114     }
115 
116     BejDictionaries dictionaries = {
117         .schemaDictionary = (*schemaDictOrErr).data(),
118         .schemaDictionarySize = (uint32_t)(*schemaDictOrErr).size_bytes(),
119         .annotationDictionary = (*annotationDictOrErr).data(),
120         .annotationDictionarySize =
121             (uint32_t)(*annotationDictOrErr).size_bytes(),
122         // We do not use the error dictionary.
123         .errorDictionary = nullptr,
124         .errorDictionarySize = 0,
125     };
126 
127     // Soon after header, we have bejLocator field. Then we have the encoded
128     // data.
129     const uint8_t* encodedPldmBlock =
130         rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
131         header->operationLocatorLength;
132 
133     // Decoded the data.
134     if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
135                                                header->requestPayloadLength)) !=
136         0)
137     {
138         stdplus::print(stderr, "BEJ decoding failed.\n");
139         return RdeDecodeStatus::RdeBejDecodingError;
140     }
141 
142     // Post the output.
143     if (!exStorer->publishJson(decoder.getOutput()))
144     {
145         stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
146         return RdeDecodeStatus::RdeExternalStorerError;
147     }
148     return RdeDecodeStatus::RdeOk;
149 }
150 
multiPartReceiveResp(std::span<const uint8_t> rdeCommand)151 RdeDecodeStatus RdeCommandHandler::multiPartReceiveResp(
152     std::span<const uint8_t> rdeCommand)
153 {
154     if (rdeCommand.size() < sizeof(MultipartReceiveResHeader))
155     {
156         stdplus::print(
157             stderr, "RDE command is smaller than the expected header size.\n");
158         return RdeDecodeStatus::RdeInvalidCommand;
159     }
160 
161     const MultipartReceiveResHeader* header =
162         reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
163 
164     if (rdeCommand.size() <
165         sizeof(MultipartReceiveResHeader) + header->dataLengthBytes)
166     {
167         stdplus::print(
168             stderr,
169             "RDE command size is smaller than header + declared payload size.\n");
170         return RdeDecodeStatus::RdeInvalidCommand;
171     }
172 
173     // This is a hack to get the resource ID for the dictionary data. Even
174     // though nextDataTransferHandle field is supposed to be used for something
175     // else, BIOS is using it to specify the resource ID corresponding to the
176     // dictionary data.
177     uint32_t resourceId = header->nextDataTransferHandle;
178 
179     // data points to the payload of the MultipartReceive.
180     const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader);
181     RdeDecodeStatus ret = RdeDecodeStatus::RdeOk;
182 
183     switch (header->transferFlag)
184     {
185         case static_cast<uint8_t>(
186             RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
187             handleFlagStart(header, data, resourceId);
188             break;
189         case static_cast<uint8_t>(
190             RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
191             ret = handleFlagMiddle(header, data, resourceId);
192             break;
193         case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
194             ret = handleFlagEnd(rdeCommand, header, data, resourceId);
195             break;
196         case static_cast<uint8_t>(
197             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
198             ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
199             break;
200         default:
201             stdplus::print(stderr, "Invalid transfer flag: {}\n",
202                            header->transferFlag);
203             ret = RdeDecodeStatus::RdeInvalidCommand;
204     }
205 
206     // If there is a failure, this assignment is not useful. So we can do it
207     // even if there is a failure.
208     prevDictResourceId = resourceId;
209     return ret;
210 }
211 
calcCrcTable()212 void RdeCommandHandler::calcCrcTable()
213 {
214     for (uint32_t i = 0; i < UINT8_MAX + 1; ++i)
215     {
216         uint32_t rem = i;
217         for (uint8_t k = 0; k < 8; ++k)
218         {
219             rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1;
220         }
221         crcTable[i] = rem;
222     }
223 }
224 
updateCrc(std::span<const uint8_t> stream)225 void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream)
226 {
227     for (uint32_t i = 0; i < stream.size_bytes(); ++i)
228     {
229         crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8);
230     }
231 }
232 
finalChecksum()233 uint32_t RdeCommandHandler::finalChecksum()
234 {
235     return (crc ^ 0xFFFFFFFF);
236 }
237 
handleCrc(std::span<const uint8_t> multiReceiveRespCmd)238 RdeDecodeStatus RdeCommandHandler::handleCrc(
239     std::span<const uint8_t> multiReceiveRespCmd)
240 {
241     const MultipartReceiveResHeader* header =
242         reinterpret_cast<const MultipartReceiveResHeader*>(
243             multiReceiveRespCmd.data());
244 
245     // Validate that the total message size (header + data + checksum) does not
246     // exceed the actual size of the received buffer.
247     size_t expectedSize = sizeof(MultipartReceiveResHeader) +
248                           header->dataLengthBytes + sizeof(uint32_t);
249     if (expectedSize != multiReceiveRespCmd.size())
250     {
251         stdplus::print(
252             stderr,
253             "Corruption detected: Invalid dataLengthBytes in header or not enough bytes for checksum.\n");
254         return RdeDecodeStatus::RdeInvalidCommand;
255     }
256 
257     const uint8_t* checksumPtr =
258         multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
259         header->dataLengthBytes;
260     uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
261                         (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
262 
263     if (finalChecksum() != checksum)
264     {
265         stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
266                        checksum, finalChecksum());
267         dictionaryManager.invalidateDictionaries();
268         return RdeDecodeStatus::RdeInvalidChecksum;
269     }
270     return RdeDecodeStatus::RdeOk;
271 }
272 
handleFlagStart(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)273 void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header,
274                                         const uint8_t* data,
275                                         uint32_t resourceId)
276 {
277     // This is a beginning of a dictionary. Reset CRC.
278     crc = 0xFFFFFFFF;
279     std::span dataS(data, header->dataLengthBytes);
280     dictionaryManager.startDictionaryEntry(resourceId, dataS);
281     // Start checksum calculation only for the data portion.
282     updateCrc(dataS);
283     flagState = RdeDictTransferFlagState::RdeStateStartRecvd;
284 }
285 
handleFlagMiddle(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)286 RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
287     const MultipartReceiveResHeader* header, const uint8_t* data,
288     uint32_t resourceId)
289 {
290     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
291     {
292         stdplus::print(
293             stderr,
294             "Invalid dictionary packet order. Need start before middle.\n");
295         return RdeDecodeStatus::RdeInvalidPktOrder;
296     }
297 
298     std::span dataS(data, header->dataLengthBytes);
299     if (prevDictResourceId != resourceId)
300     {
301         // Start of a new dictionary. Mark previous dictionary as
302         // complete.
303         dictionaryManager.markDataComplete(prevDictResourceId);
304         dictionaryManager.startDictionaryEntry(resourceId, dataS);
305     }
306     else
307     {
308         // Not a new dictionary. Add the received data to the existing
309         // dictionary.
310         if (!dictionaryManager.addDictionaryData(resourceId, dataS))
311         {
312             stdplus::print(stderr,
313                            "Failed to add dictionary data: ResourceId: {}\n",
314                            resourceId);
315             return RdeDecodeStatus::RdeDictionaryError;
316         }
317     }
318     // Continue checksum calculation only for the data portion.
319     updateCrc(dataS);
320     return RdeDecodeStatus::RdeOk;
321 }
322 
handleFlagEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)323 RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
324     std::span<const uint8_t> rdeCommand,
325     const MultipartReceiveResHeader* header, const uint8_t* data,
326     uint32_t resourceId)
327 {
328     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
329     {
330         stdplus::print(
331             stderr,
332             "Invalid dictionary packet order. Need start before middle.\n");
333         return RdeDecodeStatus::RdeInvalidPktOrder;
334     }
335     flagState = RdeDictTransferFlagState::RdeStateIdle;
336 
337     std::span dataS(data, header->dataLengthBytes);
338     if (prevDictResourceId != resourceId)
339     {
340         // Start of a new dictionary. Mark previous dictionary as
341         // complete.
342         dictionaryManager.markDataComplete(prevDictResourceId);
343         dictionaryManager.startDictionaryEntry(resourceId, dataS);
344     }
345     else
346     {
347         if (!dictionaryManager.addDictionaryData(resourceId, dataS))
348         {
349             stdplus::print(stderr,
350                            "Failed to add dictionary data: ResourceId: {}\n",
351                            resourceId);
352             return RdeDecodeStatus::RdeDictionaryError;
353         }
354     }
355     dictionaryManager.markDataComplete(resourceId);
356 
357     // Continue checksum calculation only for the data portion. At the end of
358     // data, we will have the DataIntegrityChecksum field. So omit that when
359     // calculating checksum.
360     updateCrc(dataS);
361     auto ret = handleCrc(rdeCommand);
362     if (ret != RdeDecodeStatus::RdeOk)
363     {
364         return ret;
365     }
366     return RdeDecodeStatus::RdeStopFlagReceived;
367 }
368 
handleFlagStartAndEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)369 RdeDecodeStatus RdeCommandHandler::handleFlagStartAndEnd(
370     std::span<const uint8_t> rdeCommand,
371     const MultipartReceiveResHeader* header, const uint8_t* data,
372     uint32_t resourceId)
373 {
374     // This is a beginning of a dictionary. Reset CRC.
375     crc = 0xFFFFFFFF;
376     // This is a beginning and end of a dictionary.
377     dictionaryManager.startDictionaryEntry(
378         resourceId, std::span(data, header->dataLengthBytes));
379     dictionaryManager.markDataComplete(resourceId);
380     flagState = RdeDictTransferFlagState::RdeStateIdle;
381 
382     // Do checksum calculation only for the data portion. At the end of data, we
383     // will have the DataIntegrityChecksum field. So omit that when calculating
384     // checksum.
385     updateCrc(std::span(data, header->dataLengthBytes));
386 
387     auto ret = handleCrc(rdeCommand);
388     if (ret != RdeDecodeStatus::RdeOk)
389     {
390         return ret;
391     }
392     return RdeDecodeStatus::RdeStopFlagReceived;
393 }
394 
395 } // namespace rde
396 } // namespace bios_bmc_smm_error_logger
397