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