blob: 6f6acfb57fd4846c2c9c277919966fdb5788d4f9 [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{
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
kasunathaedea9f2022-12-05 11:17:26 -080061 if (header->operationType !=
62 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate))
kasunathbac958d2022-06-07 18:15:24 -070063 {
Patrick Williams5de90612024-02-13 21:31:53 -060064 stdplus::print(stderr, "Operation not supported\n");
kasunathbac958d2022-06-07 18:15:24 -070065 return RdeDecodeStatus::RdeUnsupportedOperation;
66 }
67
68 // OperationInit payload overflows are not suported.
69 if (header->sendDataTransferHandle != 0)
70 {
Patrick Williams5de90612024-02-13 21:31:53 -060071 stdplus::print(stderr, "Payload should fit in within the request\n");
kasunathbac958d2022-06-07 18:15:24 -070072 return RdeDecodeStatus::RdePayloadOverflow;
73 }
74
75 auto schemaDictOrErr = dictionaryManager.getDictionary(header->resourceID);
76 if (!schemaDictOrErr)
77 {
Patrick Williams5de90612024-02-13 21:31:53 -060078 stdplus::print(stderr,
79 "Schema Dictionary not found for resourceId: {}\n",
80 header->resourceID);
kasunathbac958d2022-06-07 18:15:24 -070081 return RdeDecodeStatus::RdeNoDictionary;
82 }
83
84 auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary();
85 if (!annotationDictOrErr)
86 {
Patrick Williams5de90612024-02-13 21:31:53 -060087 stdplus::print(stderr, "Annotation dictionary not found\n");
kasunathbac958d2022-06-07 18:15:24 -070088 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.
Patrick Williams1a643562024-08-16 15:22:05 -0400100 const uint8_t* encodedPldmBlock =
101 rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
102 header->operationLocatorLength;
kasunathbac958d2022-06-07 18:15:24 -0700103
104 // Decoded the data.
105 if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
106 header->requestPayloadLength)) !=
107 0)
108 {
Patrick Williams5de90612024-02-13 21:31:53 -0600109 stdplus::print(stderr, "BEJ decoding failed.\n");
kasunathbac958d2022-06-07 18:15:24 -0700110 return RdeDecodeStatus::RdeBejDecodingError;
111 }
112
113 // Post the output.
114 if (!exStorer->publishJson(decoder.getOutput()))
115 {
Patrick Williams5de90612024-02-13 21:31:53 -0600116 stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
kasunathbac958d2022-06-07 18:15:24 -0700117 return RdeDecodeStatus::RdeExternalStorerError;
118 }
119 return RdeDecodeStatus::RdeOk;
120}
121
Patrick Williams99cd49a2025-03-03 11:20:29 -0500122RdeDecodeStatus RdeCommandHandler::multiPartReceiveResp(
123 std::span<const uint8_t> rdeCommand)
kasunathbac958d2022-06-07 18:15:24 -0700124{
Brandon Kim90ccfe82025-06-06 20:38:56 +0000125 if (rdeCommand.size() < sizeof(MultipartReceiveResHeader))
126 {
127 stdplus::print(
128 stderr, "RDE command is smaller than the expected header size.\n");
129 return RdeDecodeStatus::RdeInvalidCommand;
130 }
131
kasunathbac958d2022-06-07 18:15:24 -0700132 const MultipartReceiveResHeader* header =
133 reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
134
Brandon Kim90ccfe82025-06-06 20:38:56 +0000135 if (rdeCommand.size() <
136 sizeof(MultipartReceiveResHeader) + header->dataLengthBytes)
137 {
138 stdplus::print(
139 stderr,
140 "RDE command size is smaller than header + declared payload size.\n");
141 return RdeDecodeStatus::RdeInvalidCommand;
142 }
143
kasunathbac958d2022-06-07 18:15:24 -0700144 // This is a hack to get the resource ID for the dictionary data. Even
145 // though nextDataTransferHandle field is supposed to be used for something
146 // else, BIOS is using it to specify the resource ID corresponding to the
147 // dictionary data.
148 uint32_t resourceId = header->nextDataTransferHandle;
149
150 // data points to the payload of the MultipartReceive.
151 const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader);
152 RdeDecodeStatus ret = RdeDecodeStatus::RdeOk;
153
154 switch (header->transferFlag)
155 {
kasunathaedea9f2022-12-05 11:17:26 -0800156 case static_cast<uint8_t>(
157 RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
kasunathbac958d2022-06-07 18:15:24 -0700158 handleFlagStart(header, data, resourceId);
159 break;
kasunathaedea9f2022-12-05 11:17:26 -0800160 case static_cast<uint8_t>(
161 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
kasunathbac958d2022-06-07 18:15:24 -0700162 ret = handleFlagMiddle(header, data, resourceId);
163 break;
kasunathaedea9f2022-12-05 11:17:26 -0800164 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
kasunathbac958d2022-06-07 18:15:24 -0700165 ret = handleFlagEnd(rdeCommand, header, data, resourceId);
166 break;
kasunathaedea9f2022-12-05 11:17:26 -0800167 case static_cast<uint8_t>(
168 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
kasunathbac958d2022-06-07 18:15:24 -0700169 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
170 break;
171 default:
Patrick Williams5de90612024-02-13 21:31:53 -0600172 stdplus::print(stderr, "Invalid transfer flag: {}\n",
173 header->transferFlag);
kasunathbac958d2022-06-07 18:15:24 -0700174 ret = RdeDecodeStatus::RdeInvalidCommand;
175 }
176
177 // If there is a failure, this assignment is not useful. So we can do it
178 // even if there is a failure.
179 prevDictResourceId = resourceId;
180 return ret;
181}
182
183void RdeCommandHandler::calcCrcTable()
184{
185 for (uint32_t i = 0; i < UINT8_MAX + 1; ++i)
186 {
187 uint32_t rem = i;
188 for (uint8_t k = 0; k < 8; ++k)
189 {
190 rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1;
191 }
192 crcTable[i] = rem;
193 }
194}
195
196void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream)
197{
198 for (uint32_t i = 0; i < stream.size_bytes(); ++i)
199 {
200 crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8);
201 }
202}
203
204uint32_t RdeCommandHandler::finalChecksum()
205{
206 return (crc ^ 0xFFFFFFFF);
207}
208
Patrick Williams99cd49a2025-03-03 11:20:29 -0500209RdeDecodeStatus RdeCommandHandler::handleCrc(
210 std::span<const uint8_t> multiReceiveRespCmd)
kasunathbac958d2022-06-07 18:15:24 -0700211{
212 const MultipartReceiveResHeader* header =
213 reinterpret_cast<const MultipartReceiveResHeader*>(
214 multiReceiveRespCmd.data());
Patrick Williams1a643562024-08-16 15:22:05 -0400215 const uint8_t* checksumPtr =
216 multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
217 header->dataLengthBytes;
kasunathbac958d2022-06-07 18:15:24 -0700218 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
219 (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
220
221 if (finalChecksum() != checksum)
222 {
Patrick Williams5de90612024-02-13 21:31:53 -0600223 stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
224 checksum, finalChecksum());
kasunathbac958d2022-06-07 18:15:24 -0700225 dictionaryManager.invalidateDictionaries();
226 return RdeDecodeStatus::RdeInvalidChecksum;
227 }
228 return RdeDecodeStatus::RdeOk;
229}
230
231void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header,
232 const uint8_t* data,
233 uint32_t resourceId)
234{
235 // This is a beginning of a dictionary. Reset CRC.
236 crc = 0xFFFFFFFF;
237 std::span dataS(data, header->dataLengthBytes);
238 dictionaryManager.startDictionaryEntry(resourceId, dataS);
239 // Start checksum calculation only for the data portion.
240 updateCrc(dataS);
241 flagState = RdeDictTransferFlagState::RdeStateStartRecvd;
242}
243
Patrick Williams1a643562024-08-16 15:22:05 -0400244RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
245 const MultipartReceiveResHeader* header, const uint8_t* data,
246 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700247{
248 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
249 {
Patrick Williams5de90612024-02-13 21:31:53 -0600250 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700251 stderr,
252 "Invalid dictionary packet order. Need start before middle.\n");
253 return RdeDecodeStatus::RdeInvalidPktOrder;
254 }
255
256 std::span dataS(data, header->dataLengthBytes);
257 if (prevDictResourceId != resourceId)
258 {
259 // Start of a new dictionary. Mark previous dictionary as
260 // complete.
261 dictionaryManager.markDataComplete(prevDictResourceId);
262 dictionaryManager.startDictionaryEntry(resourceId, dataS);
263 }
264 else
265 {
266 // Not a new dictionary. Add the received data to the existing
267 // dictionary.
268 if (!dictionaryManager.addDictionaryData(resourceId, dataS))
269 {
Patrick Williams5de90612024-02-13 21:31:53 -0600270 stdplus::print(stderr,
271 "Failed to add dictionary data: ResourceId: {}\n",
272 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700273 return RdeDecodeStatus::RdeDictionaryError;
274 }
275 }
276 // Continue checksum calculation only for the data portion.
277 updateCrc(dataS);
278 return RdeDecodeStatus::RdeOk;
279}
280
Patrick Williams1a643562024-08-16 15:22:05 -0400281RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
282 std::span<const uint8_t> rdeCommand,
283 const MultipartReceiveResHeader* header, const uint8_t* data,
284 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700285{
286 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
287 {
Patrick Williams5de90612024-02-13 21:31:53 -0600288 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700289 stderr,
290 "Invalid dictionary packet order. Need start before middle.\n");
291 return RdeDecodeStatus::RdeInvalidPktOrder;
292 }
293 flagState = RdeDictTransferFlagState::RdeStateIdle;
294
295 std::span dataS(data, header->dataLengthBytes);
296 if (prevDictResourceId != resourceId)
297 {
298 // Start of a new dictionary. Mark previous dictionary as
299 // complete.
300 dictionaryManager.markDataComplete(prevDictResourceId);
301 dictionaryManager.startDictionaryEntry(resourceId, dataS);
302 }
303 else
304 {
305 if (!dictionaryManager.addDictionaryData(resourceId, dataS))
306 {
Patrick Williams5de90612024-02-13 21:31:53 -0600307 stdplus::print(stderr,
308 "Failed to add dictionary data: ResourceId: {}\n",
309 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700310 return RdeDecodeStatus::RdeDictionaryError;
311 }
312 }
313 dictionaryManager.markDataComplete(resourceId);
314
315 // Continue checksum calculation only for the data portion. At the end of
316 // data, we will have the DataIntegrityChecksum field. So omit that when
317 // calculating checksum.
318 updateCrc(dataS);
319 auto ret = handleCrc(rdeCommand);
320 if (ret != RdeDecodeStatus::RdeOk)
321 {
322 return ret;
323 }
324 return RdeDecodeStatus::RdeStopFlagReceived;
325}
326
327RdeDecodeStatus RdeCommandHandler::handleFlagStartAndEnd(
328 std::span<const uint8_t> rdeCommand,
329 const MultipartReceiveResHeader* header, const uint8_t* data,
330 uint32_t resourceId)
331{
332 // This is a beginning of a dictionary. Reset CRC.
333 crc = 0xFFFFFFFF;
334 // This is a beginning and end of a dictionary.
335 dictionaryManager.startDictionaryEntry(
336 resourceId, std::span(data, header->dataLengthBytes));
337 dictionaryManager.markDataComplete(resourceId);
338 flagState = RdeDictTransferFlagState::RdeStateIdle;
339
340 // Do checksum calculation only for the data portion. At the end of data, we
341 // will have the DataIntegrityChecksum field. So omit that when calculating
342 // checksum.
343 updateCrc(std::span(data, header->dataLengthBytes));
344
345 auto ret = handleCrc(rdeCommand);
346 if (ret != RdeDecodeStatus::RdeOk)
347 {
348 return ret;
349 }
350 return RdeDecodeStatus::RdeStopFlagReceived;
351}
352
353} // namespace rde
354} // namespace bios_bmc_smm_error_logger