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 .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.
125 const uint8_t* encodedPldmBlock =
126 rdeCommand.data() + sizeof(RdeOperationInitReqHeader) +
127 header->operationLocatorLength;
128
129 // Decoded the data.
130 if (decoder.decode(dictionaries, std::span(encodedPldmBlock,
131 header->requestPayloadLength)) !=
132 0)
133 {
134 stdplus::print(stderr, "BEJ decoding failed.\n");
135 return RdeDecodeStatus::RdeBejDecodingError;
136 }
137
138 // Post the output.
139 if (!exStorer->publishJson(decoder.getOutput()))
140 {
141 stdplus::print(stderr, "Failed to write to ExternalStorer.\n");
142 return RdeDecodeStatus::RdeExternalStorerError;
143 }
144 return RdeDecodeStatus::RdeOk;
145 }
146
multiPartReceiveResp(std::span<const uint8_t> rdeCommand)147 RdeDecodeStatus RdeCommandHandler::multiPartReceiveResp(
148 std::span<const uint8_t> rdeCommand)
149 {
150 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
157 const MultipartReceiveResHeader* header =
158 reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data());
159
160 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
169 // 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 {
181 case static_cast<uint8_t>(
182 RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
183 handleFlagStart(header, data, resourceId);
184 break;
185 case static_cast<uint8_t>(
186 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
187 ret = handleFlagMiddle(header, data, resourceId);
188 break;
189 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
190 ret = handleFlagEnd(rdeCommand, header, data, resourceId);
191 break;
192 case static_cast<uint8_t>(
193 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
194 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
195 break;
196 default:
197 stdplus::print(stderr, "Invalid transfer flag: {}\n",
198 header->transferFlag);
199 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
calcCrcTable()208 void 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
updateCrc(std::span<const uint8_t> stream)221 void 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
finalChecksum()229 uint32_t RdeCommandHandler::finalChecksum()
230 {
231 return (crc ^ 0xFFFFFFFF);
232 }
233
handleCrc(std::span<const uint8_t> multiReceiveRespCmd)234 RdeDecodeStatus RdeCommandHandler::handleCrc(
235 std::span<const uint8_t> multiReceiveRespCmd)
236 {
237 const MultipartReceiveResHeader* header =
238 reinterpret_cast<const MultipartReceiveResHeader*>(
239 multiReceiveRespCmd.data());
240 const uint8_t* checksumPtr =
241 multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
242 header->dataLengthBytes;
243 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
244 (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
245
246 if (finalChecksum() != checksum)
247 {
248 stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
249 checksum, finalChecksum());
250 dictionaryManager.invalidateDictionaries();
251 return RdeDecodeStatus::RdeInvalidChecksum;
252 }
253 return RdeDecodeStatus::RdeOk;
254 }
255
handleFlagStart(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)256 void 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
handleFlagMiddle(const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)269 RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
270 const MultipartReceiveResHeader* header, const uint8_t* data,
271 uint32_t resourceId)
272 {
273 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
274 {
275 stdplus::print(
276 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 {
295 stdplus::print(stderr,
296 "Failed to add dictionary data: ResourceId: {}\n",
297 resourceId);
298 return RdeDecodeStatus::RdeDictionaryError;
299 }
300 }
301 // Continue checksum calculation only for the data portion.
302 updateCrc(dataS);
303 return RdeDecodeStatus::RdeOk;
304 }
305
handleFlagEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)306 RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
307 std::span<const uint8_t> rdeCommand,
308 const MultipartReceiveResHeader* header, const uint8_t* data,
309 uint32_t resourceId)
310 {
311 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
312 {
313 stdplus::print(
314 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 {
332 stdplus::print(stderr,
333 "Failed to add dictionary data: ResourceId: {}\n",
334 resourceId);
335 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
handleFlagStartAndEnd(std::span<const uint8_t> rdeCommand,const MultipartReceiveResHeader * header,const uint8_t * data,uint32_t resourceId)352 RdeDecodeStatus 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
380