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