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