blob: 74f62040c47701f903e3a9251495dc2b57e63d8e [file] [log] [blame]
kasunathbac958d2022-06-07 18:15:24 -07001#include "rde/rde_handler.hpp"
2
Patrick Williams5de90612024-02-13 21:31:53 -06003#include <stdplus/print.hpp>
kasunathbac958d2022-06-07 18:15:24 -07004
Patrick Williams5de90612024-02-13 21:31:53 -06005#include <format>
kasunathbac958d2022-06-07 18:15:24 -07006#include <iostream>
7
8namespace bios_bmc_smm_error_logger
9{
10namespace rde
11{
12
13/**
14 * @brief CRC-32 divisor.
15 *
16 * This is equivalent to the one used by IEEE802.3.
17 */
18constexpr uint32_t crcDevisor = 0xedb88320;
19
20RdeCommandHandler::RdeCommandHandler(
21 std::unique_ptr<ExternalStorerInterface> exStorer) :
22 flagState(RdeDictTransferFlagState::RdeStateIdle),
Brandon Kim90ccfe82025-06-06 20:38:56 +000023 exStorer(std::move(exStorer)), prevDictResourceId(0), crc(0xFFFFFFFF)
kasunathbac958d2022-06-07 18:15:24 -070024{
25 // Initialize CRC table.
26 calcCrcTable();
27}
28
Patrick Williams1a643562024-08-16 15:22:05 -040029RdeDecodeStatus RdeCommandHandler::decodeRdeCommand(
30 std::span<const uint8_t> rdeCommand, RdeCommandType type)
kasunathbac958d2022-06-07 18:15:24 -070031{
32 if (type == RdeCommandType::RdeMultiPartReceiveResponse)
33 {
34 return multiPartReceiveResp(rdeCommand);
35 }
36 if (type == RdeCommandType::RdeOperationInitRequest)
37 {
38 return operationInitRequest(rdeCommand);
39 }
40
Patrick Williams5de90612024-02-13 21:31:53 -060041 stdplus::print(stderr, "Invalid command type\n");
kasunathbac958d2022-06-07 18:15:24 -070042 return RdeDecodeStatus::RdeInvalidCommand;
43}
44
45uint32_t RdeCommandHandler::getDictionaryCount()
46{
47 return dictionaryManager.getDictionaryCount();
48}
49
Patrick Williams99cd49a2025-03-03 11:20:29 -050050RdeDecodeStatus RdeCommandHandler::operationInitRequest(
51 std::span<const uint8_t> rdeCommand)
kasunathbac958d2022-06-07 18:15:24 -070052{
Brandon Kim8540c7d2025-06-08 23:54:24 +000053 // 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
kasunathbac958d2022-06-07 18:15:24 -070063 const RdeOperationInitReqHeader* header =
64 reinterpret_cast<const RdeOperationInitReqHeader*>(rdeCommand.data());
Brandon Kim8540c7d2025-06-08 23:54:24 +000065
kasunathbac958d2022-06-07 18:15:24 -070066 // Check if there is a payload. If not, we are not doing anything.
67 if (!header->containsRequestPayload)
68 {
69 return RdeDecodeStatus::RdeOk;
70 }
71
Brandon Kim8540c7d2025-06-08 23:54:24 +000072 // 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
kasunathaedea9f2022-12-05 11:17:26 -080086 if (header->operationType !=
87 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate))
kasunathbac958d2022-06-07 18:15:24 -070088 {
Patrick Williams5de90612024-02-13 21:31:53 -060089 stdplus::print(stderr, "Operation not supported\n");
kasunathbac958d2022-06-07 18:15:24 -070090 return RdeDecodeStatus::RdeUnsupportedOperation;
91 }
92
93 // OperationInit payload overflows are not suported.
94 if (header->sendDataTransferHandle != 0)
95 {
Patrick Williams5de90612024-02-13 21:31:53 -060096 stdplus::print(stderr, "Payload should fit in within the request\n");
kasunathbac958d2022-06-07 18:15:24 -070097 return RdeDecodeStatus::RdePayloadOverflow;
98 }
99
100 auto schemaDictOrErr = dictionaryManager.getDictionary(header->resourceID);
101 if (!schemaDictOrErr)
102 {
Patrick Williams5de90612024-02-13 21:31:53 -0600103 stdplus::print(stderr,
104 "Schema Dictionary not found for resourceId: {}\n",
105 header->resourceID);
kasunathbac958d2022-06-07 18:15:24 -0700106 return RdeDecodeStatus::RdeNoDictionary;
107 }
108
109 auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary();
110 if (!annotationDictOrErr)
111 {
Patrick Williams5de90612024-02-13 21:31:53 -0600112 stdplus::print(stderr, "Annotation dictionary not found\n");
kasunathbac958d2022-06-07 18:15:24 -0700113 return RdeDecodeStatus::RdeNoDictionary;
114 }
115
116 BejDictionaries dictionaries = {
117 .schemaDictionary = (*schemaDictOrErr).data(),
118 .annotationDictionary = (*annotationDictOrErr).data(),
119 // We do not use the error dictionary.
120 .errorDictionary = nullptr,
121 };
122
123 // Soon after header, we have bejLocator field. Then we have the encoded
124 // data.
Patrick Williams1a643562024-08-16 15:22:05 -0400125 const uint8_t* encodedPldmBlock =
126 rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
127 header->operationLocatorLength;
kasunathbac958d2022-06-07 18:15:24 -0700128
129 // Decoded the data.
130 if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
131 header->requestPayloadLength)) !=
132 0)
133 {
Patrick Williams5de90612024-02-13 21:31:53 -0600134 stdplus::print(stderr, "BEJ decoding failed.\n");
kasunathbac958d2022-06-07 18:15:24 -0700135 return RdeDecodeStatus::RdeBejDecodingError;
136 }
137
138 // Post the output.
139 if (!exStorer->publishJson(decoder.getOutput()))
140 {
Patrick Williams5de90612024-02-13 21:31:53 -0600141 stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
kasunathbac958d2022-06-07 18:15:24 -0700142 return RdeDecodeStatus::RdeExternalStorerError;
143 }
144 return RdeDecodeStatus::RdeOk;
145}
146
Patrick Williams99cd49a2025-03-03 11:20:29 -0500147RdeDecodeStatus RdeCommandHandler::multiPartReceiveResp(
148 std::span<const uint8_t> rdeCommand)
kasunathbac958d2022-06-07 18:15:24 -0700149{
Brandon Kim90ccfe82025-06-06 20:38:56 +0000150 if (rdeCommand.size() < sizeof(MultipartReceiveResHeader))
151 {
152 stdplus::print(
153 stderr, "RDE command is smaller than the expected header size.\n");
154 return RdeDecodeStatus::RdeInvalidCommand;
155 }
156
kasunathbac958d2022-06-07 18:15:24 -0700157 const MultipartReceiveResHeader* header =
158 reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
159
Brandon Kim90ccfe82025-06-06 20:38:56 +0000160 if (rdeCommand.size() <
161 sizeof(MultipartReceiveResHeader) + header->dataLengthBytes)
162 {
163 stdplus::print(
164 stderr,
165 "RDE command size is smaller than header + declared payload size.\n");
166 return RdeDecodeStatus::RdeInvalidCommand;
167 }
168
kasunathbac958d2022-06-07 18:15:24 -0700169 // This is a hack to get the resource ID for the dictionary data. Even
170 // though nextDataTransferHandle field is supposed to be used for something
171 // else, BIOS is using it to specify the resource ID corresponding to the
172 // dictionary data.
173 uint32_t resourceId = header->nextDataTransferHandle;
174
175 // data points to the payload of the MultipartReceive.
176 const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader);
177 RdeDecodeStatus ret = RdeDecodeStatus::RdeOk;
178
179 switch (header->transferFlag)
180 {
kasunathaedea9f2022-12-05 11:17:26 -0800181 case static_cast<uint8_t>(
182 RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
kasunathbac958d2022-06-07 18:15:24 -0700183 handleFlagStart(header, data, resourceId);
184 break;
kasunathaedea9f2022-12-05 11:17:26 -0800185 case static_cast<uint8_t>(
186 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
kasunathbac958d2022-06-07 18:15:24 -0700187 ret = handleFlagMiddle(header, data, resourceId);
188 break;
kasunathaedea9f2022-12-05 11:17:26 -0800189 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
kasunathbac958d2022-06-07 18:15:24 -0700190 ret = handleFlagEnd(rdeCommand, header, data, resourceId);
191 break;
kasunathaedea9f2022-12-05 11:17:26 -0800192 case static_cast<uint8_t>(
193 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
kasunathbac958d2022-06-07 18:15:24 -0700194 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
195 break;
196 default:
Patrick Williams5de90612024-02-13 21:31:53 -0600197 stdplus::print(stderr, "Invalid transfer flag: {}\n",
198 header->transferFlag);
kasunathbac958d2022-06-07 18:15:24 -0700199 ret = RdeDecodeStatus::RdeInvalidCommand;
200 }
201
202 // If there is a failure, this assignment is not useful. So we can do it
203 // even if there is a failure.
204 prevDictResourceId = resourceId;
205 return ret;
206}
207
208void RdeCommandHandler::calcCrcTable()
209{
210 for (uint32_t i = 0; i < UINT8_MAX + 1; ++i)
211 {
212 uint32_t rem = i;
213 for (uint8_t k = 0; k < 8; ++k)
214 {
215 rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1;
216 }
217 crcTable[i] = rem;
218 }
219}
220
221void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream)
222{
223 for (uint32_t i = 0; i < stream.size_bytes(); ++i)
224 {
225 crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8);
226 }
227}
228
229uint32_t RdeCommandHandler::finalChecksum()
230{
231 return (crc ^ 0xFFFFFFFF);
232}
233
Patrick Williams99cd49a2025-03-03 11:20:29 -0500234RdeDecodeStatus RdeCommandHandler::handleCrc(
235 std::span<const uint8_t> multiReceiveRespCmd)
kasunathbac958d2022-06-07 18:15:24 -0700236{
237 const MultipartReceiveResHeader* header =
238 reinterpret_cast<const MultipartReceiveResHeader*>(
239 multiReceiveRespCmd.data());
Patrick Williams1a643562024-08-16 15:22:05 -0400240 const uint8_t* checksumPtr =
241 multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
242 header->dataLengthBytes;
kasunathbac958d2022-06-07 18:15:24 -0700243 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
244 (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
245
246 if (finalChecksum() != checksum)
247 {
Patrick Williams5de90612024-02-13 21:31:53 -0600248 stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
249 checksum, finalChecksum());
kasunathbac958d2022-06-07 18:15:24 -0700250 dictionaryManager.invalidateDictionaries();
251 return RdeDecodeStatus::RdeInvalidChecksum;
252 }
253 return RdeDecodeStatus::RdeOk;
254}
255
256void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header,
257 const uint8_t* data,
258 uint32_t resourceId)
259{
260 // This is a beginning of a dictionary. Reset CRC.
261 crc = 0xFFFFFFFF;
262 std::span dataS(data, header->dataLengthBytes);
263 dictionaryManager.startDictionaryEntry(resourceId, dataS);
264 // Start checksum calculation only for the data portion.
265 updateCrc(dataS);
266 flagState = RdeDictTransferFlagState::RdeStateStartRecvd;
267}
268
Patrick Williams1a643562024-08-16 15:22:05 -0400269RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
270 const MultipartReceiveResHeader* header, const uint8_t* data,
271 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700272{
273 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
274 {
Patrick Williams5de90612024-02-13 21:31:53 -0600275 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700276 stderr,
277 "Invalid dictionary packet order. Need start before middle.\n");
278 return RdeDecodeStatus::RdeInvalidPktOrder;
279 }
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 // Not a new dictionary. Add the received data to the existing
292 // dictionary.
293 if (!dictionaryManager.addDictionaryData(resourceId, dataS))
294 {
Patrick Williams5de90612024-02-13 21:31:53 -0600295 stdplus::print(stderr,
296 "Failed to add dictionary data: ResourceId: {}\n",
297 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700298 return RdeDecodeStatus::RdeDictionaryError;
299 }
300 }
301 // Continue checksum calculation only for the data portion.
302 updateCrc(dataS);
303 return RdeDecodeStatus::RdeOk;
304}
305
Patrick Williams1a643562024-08-16 15:22:05 -0400306RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
307 std::span<const uint8_t> rdeCommand,
308 const MultipartReceiveResHeader* header, const uint8_t* data,
309 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700310{
311 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
312 {
Patrick Williams5de90612024-02-13 21:31:53 -0600313 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700314 stderr,
315 "Invalid dictionary packet order. Need start before middle.\n");
316 return RdeDecodeStatus::RdeInvalidPktOrder;
317 }
318 flagState = RdeDictTransferFlagState::RdeStateIdle;
319
320 std::span dataS(data, header->dataLengthBytes);
321 if (prevDictResourceId != resourceId)
322 {
323 // Start of a new dictionary. Mark previous dictionary as
324 // complete.
325 dictionaryManager.markDataComplete(prevDictResourceId);
326 dictionaryManager.startDictionaryEntry(resourceId, dataS);
327 }
328 else
329 {
330 if (!dictionaryManager.addDictionaryData(resourceId, dataS))
331 {
Patrick Williams5de90612024-02-13 21:31:53 -0600332 stdplus::print(stderr,
333 "Failed to add dictionary data: ResourceId: {}\n",
334 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700335 return RdeDecodeStatus::RdeDictionaryError;
336 }
337 }
338 dictionaryManager.markDataComplete(resourceId);
339
340 // Continue checksum calculation only for the data portion. At the end of
341 // data, we will have the DataIntegrityChecksum field. So omit that when
342 // calculating checksum.
343 updateCrc(dataS);
344 auto ret = handleCrc(rdeCommand);
345 if (ret != RdeDecodeStatus::RdeOk)
346 {
347 return ret;
348 }
349 return RdeDecodeStatus::RdeStopFlagReceived;
350}
351
352RdeDecodeStatus RdeCommandHandler::handleFlagStartAndEnd(
353 std::span<const uint8_t> rdeCommand,
354 const MultipartReceiveResHeader* header, const uint8_t* data,
355 uint32_t resourceId)
356{
357 // This is a beginning of a dictionary. Reset CRC.
358 crc = 0xFFFFFFFF;
359 // This is a beginning and end of a dictionary.
360 dictionaryManager.startDictionaryEntry(
361 resourceId, std::span(data, header->dataLengthBytes));
362 dictionaryManager.markDataComplete(resourceId);
363 flagState = RdeDictTransferFlagState::RdeStateIdle;
364
365 // Do checksum calculation only for the data portion. At the end of data, we
366 // will have the DataIntegrityChecksum field. So omit that when calculating
367 // checksum.
368 updateCrc(std::span(data, header->dataLengthBytes));
369
370 auto ret = handleCrc(rdeCommand);
371 if (ret != RdeDecodeStatus::RdeOk)
372 {
373 return ret;
374 }
375 return RdeDecodeStatus::RdeStopFlagReceived;
376}
377
378} // namespace rde
379} // namespace bios_bmc_smm_error_logger