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 
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 
50 RdeDecodeStatus
operationInitRequest(std::span<const uint8_t> rdeCommand)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         stdplus::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         stdplus::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         stdplus::print(stderr,
79                        "Schema Dictionary not found for resourceId: {}\n",
80                        header->resourceID);
81         return RdeDecodeStatus::RdeNoDictionary;
82     }
83 
84     auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary();
85     if (!annotationDictOrErr)
86     {
87         stdplus::print(stderr, "Annotation dictionary not found\n");
88         return RdeDecodeStatus::RdeNoDictionary;
89     }
90 
91     BejDictionaries dictionaries = {
92         .schemaDictionary = (*schemaDictOrErr).data(),
93         .annotationDictionary = (*annotationDictOrErr).data(),
94         // We do not use the error dictionary.
95         .errorDictionary = nullptr,
96     };
97 
98     // Soon after header, we have bejLocator field. Then we have the encoded
99     // data.
100     const uint8_t* encodedPldmBlock =
101         rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
102         header->operationLocatorLength;
103 
104     // Decoded the data.
105     if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
106                                                header->requestPayloadLength)) !=
107         0)
108     {
109         stdplus::print(stderr, "BEJ decoding failed.\n");
110         return RdeDecodeStatus::RdeBejDecodingError;
111     }
112 
113     // Post the output.
114     if (!exStorer->publishJson(decoder.getOutput()))
115     {
116         stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
117         return RdeDecodeStatus::RdeExternalStorerError;
118     }
119     return RdeDecodeStatus::RdeOk;
120 }
121 
122 RdeDecodeStatus
multiPartReceiveResp(std::span<const uint8_t> rdeCommand)123     RdeCommandHandler::multiPartReceiveResp(std::span<const uint8_t> rdeCommand)
124 {
125     const MultipartReceiveResHeader* header =
126         reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
127 
128     // This is a hack to get the resource ID for the dictionary data. Even
129     // though nextDataTransferHandle field is supposed to be used for something
130     // else, BIOS is using it to specify the resource ID corresponding to the
131     // dictionary data.
132     uint32_t resourceId = header->nextDataTransferHandle;
133 
134     // data points to the payload of the MultipartReceive.
135     const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader);
136     RdeDecodeStatus ret = RdeDecodeStatus::RdeOk;
137 
138     switch (header->transferFlag)
139     {
140         case static_cast<uint8_t>(
141             RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
142             handleFlagStart(header, data, resourceId);
143             break;
144         case static_cast<uint8_t>(
145             RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
146             ret = handleFlagMiddle(header, data, resourceId);
147             break;
148         case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
149             ret = handleFlagEnd(rdeCommand, header, data, resourceId);
150             break;
151         case static_cast<uint8_t>(
152             RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
153             ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
154             break;
155         default:
156             stdplus::print(stderr, "Invalid transfer flag: {}\n",
157                            header->transferFlag);
158             ret = RdeDecodeStatus::RdeInvalidCommand;
159     }
160 
161     // If there is a failure, this assignment is not useful. So we can do it
162     // even if there is a failure.
163     prevDictResourceId = resourceId;
164     return ret;
165 }
166 
calcCrcTable()167 void RdeCommandHandler::calcCrcTable()
168 {
169     for (uint32_t i = 0; i < UINT8_MAX + 1; ++i)
170     {
171         uint32_t rem = i;
172         for (uint8_t k = 0; k < 8; ++k)
173         {
174             rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1;
175         }
176         crcTable[i] = rem;
177     }
178 }
179 
updateCrc(std::span<const uint8_t> stream)180 void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream)
181 {
182     for (uint32_t i = 0; i < stream.size_bytes(); ++i)
183     {
184         crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8);
185     }
186 }
187 
finalChecksum()188 uint32_t RdeCommandHandler::finalChecksum()
189 {
190     return (crc ^ 0xFFFFFFFF);
191 }
192 
193 RdeDecodeStatus
handleCrc(std::span<const uint8_t> multiReceiveRespCmd)194     RdeCommandHandler::handleCrc(std::span<const uint8_t> multiReceiveRespCmd)
195 {
196     const MultipartReceiveResHeader* header =
197         reinterpret_cast<const MultipartReceiveResHeader*>(
198             multiReceiveRespCmd.data());
199     const uint8_t* checksumPtr =
200         multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
201         header->dataLengthBytes;
202     uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
203                         (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
204 
205     if (finalChecksum() != checksum)
206     {
207         stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
208                        checksum, finalChecksum());
209         dictionaryManager.invalidateDictionaries();
210         return RdeDecodeStatus::RdeInvalidChecksum;
211     }
212     return RdeDecodeStatus::RdeOk;
213 }
214 
handleFlagStart(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)215 void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header,
216                                         const uint8_t* data,
217                                         uint32_t resourceId)
218 {
219     // This is a beginning of a dictionary. Reset CRC.
220     crc = 0xFFFFFFFF;
221     std::span dataS(data, header->dataLengthBytes);
222     dictionaryManager.startDictionaryEntry(resourceId, dataS);
223     // Start checksum calculation only for the data portion.
224     updateCrc(dataS);
225     flagState = RdeDictTransferFlagState::RdeStateStartRecvd;
226 }
227 
handleFlagMiddle(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)228 RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
229     const MultipartReceiveResHeader* header, const uint8_t* data,
230     uint32_t resourceId)
231 {
232     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
233     {
234         stdplus::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             stdplus::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 
handleFlagEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)265 RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
266     std::span<const uint8_t> rdeCommand,
267     const MultipartReceiveResHeader* header, const uint8_t* data,
268     uint32_t resourceId)
269 {
270     if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
271     {
272         stdplus::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             stdplus::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 
handleFlagStartAndEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)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