xref: /openbmc/bios-bmc-smm-error-logger/src/rde/rde_handler.cpp (revision 8540c7dd20de35cd133c8e79caca04e81789a9cf)
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         .annotationDictionary = (*annotationDictOrErr).data(),
119         // We do not use the error dictionary.
120         .errorDictionary = nullptr,
121     };
122 
123     // Soon after header, we have bejLocator field. Then we have the encoded
124     // data.
125     const uint8_t* encodedPldmBlock =
126         rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
127         header->operationLocatorLength;
128 
129     // Decoded the data.
130     if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
131                                                header->requestPayloadLength)) !=
132         0)
133     {
134         stdplus::print(stderr, "BEJ decoding failed.\n");
135         return RdeDecodeStatus::RdeBejDecodingError;
136     }
137 
138     // Post the output.
139     if (!exStorer->publishJson(decoder.getOutput()))
140     {
141         stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
142         return RdeDecodeStatus::RdeExternalStorerError;
143     }
144     return RdeDecodeStatus::RdeOk;
145 }
146 
multiPartReceiveResp(std::span<const uint8_t> rdeCommand)147 RdeDecodeStatus RdeCommandHandler::multiPartReceiveResp(
148     std::span<const uint8_t> rdeCommand)
149 {
150     if (rdeCommand.size() < sizeof(MultipartReceiveResHeader))
151     {
152         stdplus::print(
153             stderr, "RDE command is smaller than the expected header size.\n");
154         return RdeDecodeStatus::RdeInvalidCommand;
155     }
156 
157     const MultipartReceiveResHeader* header =
158         reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
159 
160     if (rdeCommand.size() <
161         sizeof(MultipartReceiveResHeader) + header->dataLengthBytes)
162     {
163         stdplus::print(
164             stderr,
165             "RDE command size is smaller than header + declared payload size.\n");
166         return RdeDecodeStatus::RdeInvalidCommand;
167     }
168 
169     // This is a hack to get the resource ID for the dictionary data. Even
170     // though nextDataTransferHandle field is supposed to be used for something
171     // else, BIOS is using it to specify the resource ID corresponding to the
172     // dictionary data.
173     uint32_t resourceId = header->nextDataTransferHandle;
174 
175     // data points to the payload of the MultipartReceive.
176     const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader);
177     RdeDecodeStatus ret = RdeDecodeStatus::RdeOk;
178 
179     switch (header->transferFlag)
180     {
181         case static_cast<uint8_t>(
182             RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
183             handleFlagStart(header, data, resourceId);
184             break;
185         case static_cast<uint8_t>(
186             RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
187             ret = handleFlagMiddle(header, data, resourceId);
188             break;
189         case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
190             ret = handleFlagEnd(rdeCommand, header, data, resourceId);
191             break;
192         case static_cast<uint8_t>(
193             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
194             ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
195             break;
196         default:
197             stdplus::print(stderr, "Invalid transfer flag: {}\n",
198                            header->transferFlag);
199             ret = RdeDecodeStatus::RdeInvalidCommand;
200     }
201 
202     // If there is a failure, this assignment is not useful. So we can do it
203     // even if there is a failure.
204     prevDictResourceId = resourceId;
205     return ret;
206 }
207 
calcCrcTable()208 void RdeCommandHandler::calcCrcTable()
209 {
210     for (uint32_t i = 0; i < UINT8_MAX + 1; ++i)
211     {
212         uint32_t rem = i;
213         for (uint8_t k = 0; k < 8; ++k)
214         {
215             rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1;
216         }
217         crcTable[i] = rem;
218     }
219 }
220 
updateCrc(std::span<const uint8_t> stream)221 void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream)
222 {
223     for (uint32_t i = 0; i < stream.size_bytes(); ++i)
224     {
225         crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8);
226     }
227 }
228 
finalChecksum()229 uint32_t RdeCommandHandler::finalChecksum()
230 {
231     return (crc ^ 0xFFFFFFFF);
232 }
233 
handleCrc(std::span<const uint8_t> multiReceiveRespCmd)234 RdeDecodeStatus RdeCommandHandler::handleCrc(
235     std::span<const uint8_t> multiReceiveRespCmd)
236 {
237     const MultipartReceiveResHeader* header =
238         reinterpret_cast<const MultipartReceiveResHeader*>(
239             multiReceiveRespCmd.data());
240     const uint8_t* checksumPtr =
241         multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
242         header->dataLengthBytes;
243     uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
244                         (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
245 
246     if (finalChecksum() != checksum)
247     {
248         stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
249                        checksum, finalChecksum());
250         dictionaryManager.invalidateDictionaries();
251         return RdeDecodeStatus::RdeInvalidChecksum;
252     }
253     return RdeDecodeStatus::RdeOk;
254 }
255 
handleFlagStart(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)256 void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header,
257                                         const uint8_t* data,
258                                         uint32_t resourceId)
259 {
260     // This is a beginning of a dictionary. Reset CRC.
261     crc = 0xFFFFFFFF;
262     std::span dataS(data, header->dataLengthBytes);
263     dictionaryManager.startDictionaryEntry(resourceId, dataS);
264     // Start checksum calculation only for the data portion.
265     updateCrc(dataS);
266     flagState = RdeDictTransferFlagState::RdeStateStartRecvd;
267 }
268 
handleFlagMiddle(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)269 RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
270     const MultipartReceiveResHeader* header, const uint8_t* data,
271     uint32_t resourceId)
272 {
273     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
274     {
275         stdplus::print(
276             stderr,
277             "Invalid dictionary packet order. Need start before middle.\n");
278         return RdeDecodeStatus::RdeInvalidPktOrder;
279     }
280 
281     std::span dataS(data, header->dataLengthBytes);
282     if (prevDictResourceId != resourceId)
283     {
284         // Start of a new dictionary. Mark previous dictionary as
285         // complete.
286         dictionaryManager.markDataComplete(prevDictResourceId);
287         dictionaryManager.startDictionaryEntry(resourceId, dataS);
288     }
289     else
290     {
291         // Not a new dictionary. Add the received data to the existing
292         // dictionary.
293         if (!dictionaryManager.addDictionaryData(resourceId, dataS))
294         {
295             stdplus::print(stderr,
296                            "Failed to add dictionary data: ResourceId: {}\n",
297                            resourceId);
298             return RdeDecodeStatus::RdeDictionaryError;
299         }
300     }
301     // Continue checksum calculation only for the data portion.
302     updateCrc(dataS);
303     return RdeDecodeStatus::RdeOk;
304 }
305 
handleFlagEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)306 RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
307     std::span<const uint8_t> rdeCommand,
308     const MultipartReceiveResHeader* header, const uint8_t* data,
309     uint32_t resourceId)
310 {
311     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
312     {
313         stdplus::print(
314             stderr,
315             "Invalid dictionary packet order. Need start before middle.\n");
316         return RdeDecodeStatus::RdeInvalidPktOrder;
317     }
318     flagState = RdeDictTransferFlagState::RdeStateIdle;
319 
320     std::span dataS(data, header->dataLengthBytes);
321     if (prevDictResourceId != resourceId)
322     {
323         // Start of a new dictionary. Mark previous dictionary as
324         // complete.
325         dictionaryManager.markDataComplete(prevDictResourceId);
326         dictionaryManager.startDictionaryEntry(resourceId, dataS);
327     }
328     else
329     {
330         if (!dictionaryManager.addDictionaryData(resourceId, dataS))
331         {
332             stdplus::print(stderr,
333                            "Failed to add dictionary data: ResourceId: {}\n",
334                            resourceId);
335             return RdeDecodeStatus::RdeDictionaryError;
336         }
337     }
338     dictionaryManager.markDataComplete(resourceId);
339 
340     // Continue checksum calculation only for the data portion. At the end of
341     // data, we will have the DataIntegrityChecksum field. So omit that when
342     // calculating checksum.
343     updateCrc(dataS);
344     auto ret = handleCrc(rdeCommand);
345     if (ret != RdeDecodeStatus::RdeOk)
346     {
347         return ret;
348     }
349     return RdeDecodeStatus::RdeStopFlagReceived;
350 }
351 
handleFlagStartAndEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)352 RdeDecodeStatus RdeCommandHandler::handleFlagStartAndEnd(
353     std::span<const uint8_t> rdeCommand,
354     const MultipartReceiveResHeader* header, const uint8_t* data,
355     uint32_t resourceId)
356 {
357     // This is a beginning of a dictionary. Reset CRC.
358     crc = 0xFFFFFFFF;
359     // This is a beginning and end of a dictionary.
360     dictionaryManager.startDictionaryEntry(
361         resourceId, std::span(data, header->dataLengthBytes));
362     dictionaryManager.markDataComplete(resourceId);
363     flagState = RdeDictTransferFlagState::RdeStateIdle;
364 
365     // Do checksum calculation only for the data portion. At the end of data, we
366     // will have the DataIntegrityChecksum field. So omit that when calculating
367     // checksum.
368     updateCrc(std::span(data, header->dataLengthBytes));
369 
370     auto ret = handleCrc(rdeCommand);
371     if (ret != RdeDecodeStatus::RdeOk)
372     {
373         return ret;
374     }
375     return RdeDecodeStatus::RdeStopFlagReceived;
376 }
377 
378 } // namespace rde
379 } // namespace bios_bmc_smm_error_logger
380