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