blob: 2b8a1e4848b819f928cee0d51205e6c96ff5959e [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),
23 exStorer(std::move(exStorer))
24{
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
50RdeDecodeStatus
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
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
122RdeDecodeStatus
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 {
kasunathaedea9f2022-12-05 11:17:26 -0800140 case static_cast<uint8_t>(
141 RdeMultiReceiveTransferFlag::RdeMRecFlagStart):
kasunathbac958d2022-06-07 18:15:24 -0700142 handleFlagStart(header, data, resourceId);
143 break;
kasunathaedea9f2022-12-05 11:17:26 -0800144 case static_cast<uint8_t>(
145 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle):
kasunathbac958d2022-06-07 18:15:24 -0700146 ret = handleFlagMiddle(header, data, resourceId);
147 break;
kasunathaedea9f2022-12-05 11:17:26 -0800148 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd):
kasunathbac958d2022-06-07 18:15:24 -0700149 ret = handleFlagEnd(rdeCommand, header, data, resourceId);
150 break;
kasunathaedea9f2022-12-05 11:17:26 -0800151 case static_cast<uint8_t>(
152 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd):
kasunathbac958d2022-06-07 18:15:24 -0700153 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId);
154 break;
155 default:
Patrick Williams5de90612024-02-13 21:31:53 -0600156 stdplus::print(stderr, "Invalid transfer flag: {}\n",
157 header->transferFlag);
kasunathbac958d2022-06-07 18:15:24 -0700158 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
167void 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
180void 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
188uint32_t RdeCommandHandler::finalChecksum()
189{
190 return (crc ^ 0xFFFFFFFF);
191}
192
193RdeDecodeStatus
194 RdeCommandHandler::handleCrc(std::span<const uint8_t> multiReceiveRespCmd)
195{
196 const MultipartReceiveResHeader* header =
197 reinterpret_cast<const MultipartReceiveResHeader*>(
198 multiReceiveRespCmd.data());
Patrick Williams1a643562024-08-16 15:22:05 -0400199 const uint8_t* checksumPtr =
200 multiReceiveRespCmd.data() + sizeof(MultipartReceiveResHeader) +
201 header->dataLengthBytes;
kasunathbac958d2022-06-07 18:15:24 -0700202 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) |
203 (checksumPtr[2] << 16) | (checksumPtr[3] << 24);
204
205 if (finalChecksum() != checksum)
206 {
Patrick Williams5de90612024-02-13 21:31:53 -0600207 stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n",
208 checksum, finalChecksum());
kasunathbac958d2022-06-07 18:15:24 -0700209 dictionaryManager.invalidateDictionaries();
210 return RdeDecodeStatus::RdeInvalidChecksum;
211 }
212 return RdeDecodeStatus::RdeOk;
213}
214
215void 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
Patrick Williams1a643562024-08-16 15:22:05 -0400228RdeDecodeStatus RdeCommandHandler::handleFlagMiddle(
229 const MultipartReceiveResHeader* header, const uint8_t* data,
230 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700231{
232 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
233 {
Patrick Williams5de90612024-02-13 21:31:53 -0600234 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700235 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 {
Patrick Williams5de90612024-02-13 21:31:53 -0600254 stdplus::print(stderr,
255 "Failed to add dictionary data: ResourceId: {}\n",
256 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700257 return RdeDecodeStatus::RdeDictionaryError;
258 }
259 }
260 // Continue checksum calculation only for the data portion.
261 updateCrc(dataS);
262 return RdeDecodeStatus::RdeOk;
263}
264
Patrick Williams1a643562024-08-16 15:22:05 -0400265RdeDecodeStatus RdeCommandHandler::handleFlagEnd(
266 std::span<const uint8_t> rdeCommand,
267 const MultipartReceiveResHeader* header, const uint8_t* data,
268 uint32_t resourceId)
kasunathbac958d2022-06-07 18:15:24 -0700269{
270 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd)
271 {
Patrick Williams5de90612024-02-13 21:31:53 -0600272 stdplus::print(
kasunathbac958d2022-06-07 18:15:24 -0700273 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 {
Patrick Williams5de90612024-02-13 21:31:53 -0600291 stdplus::print(stderr,
292 "Failed to add dictionary data: ResourceId: {}\n",
293 resourceId);
kasunathbac958d2022-06-07 18:15:24 -0700294 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
311RdeDecodeStatus 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