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 20 RdeCommandHandler::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 29 RdeDecodeStatus 30 RdeCommandHandler::decodeRdeCommand(std::span<const uint8_t> rdeCommand, 31 RdeCommandType type) 32 { 33 if (type == RdeCommandType::RdeMultiPartReceiveResponse) 34 { 35 return multiPartReceiveResp(rdeCommand); 36 } 37 if (type == RdeCommandType::RdeOperationInitRequest) 38 { 39 return operationInitRequest(rdeCommand); 40 } 41 42 stdplus::print(stderr, "Invalid command type\n"); 43 return RdeDecodeStatus::RdeInvalidCommand; 44 } 45 46 uint32_t RdeCommandHandler::getDictionaryCount() 47 { 48 return dictionaryManager.getDictionaryCount(); 49 } 50 51 RdeDecodeStatus 52 RdeCommandHandler::operationInitRequest(std::span<const uint8_t> rdeCommand) 53 { 54 const RdeOperationInitReqHeader* header = 55 reinterpret_cast<const RdeOperationInitReqHeader*>(rdeCommand.data()); 56 // Check if there is a payload. If not, we are not doing anything. 57 if (!header->containsRequestPayload) 58 { 59 return RdeDecodeStatus::RdeOk; 60 } 61 62 if (header->operationType != 63 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate)) 64 { 65 stdplus::print(stderr, "Operation not supported\n"); 66 return RdeDecodeStatus::RdeUnsupportedOperation; 67 } 68 69 // OperationInit payload overflows are not suported. 70 if (header->sendDataTransferHandle != 0) 71 { 72 stdplus::print(stderr, "Payload should fit in within the request\n"); 73 return RdeDecodeStatus::RdePayloadOverflow; 74 } 75 76 auto schemaDictOrErr = dictionaryManager.getDictionary(header->resourceID); 77 if (!schemaDictOrErr) 78 { 79 stdplus::print(stderr, 80 "Schema Dictionary not found for resourceId: {}\n", 81 header->resourceID); 82 return RdeDecodeStatus::RdeNoDictionary; 83 } 84 85 auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary(); 86 if (!annotationDictOrErr) 87 { 88 stdplus::print(stderr, "Annotation dictionary not found\n"); 89 return RdeDecodeStatus::RdeNoDictionary; 90 } 91 92 BejDictionaries dictionaries = { 93 .schemaDictionary = (*schemaDictOrErr).data(), 94 .annotationDictionary = (*annotationDictOrErr).data(), 95 // We do not use the error dictionary. 96 .errorDictionary = nullptr, 97 }; 98 99 // Soon after header, we have bejLocator field. Then we have the encoded 100 // data. 101 const uint8_t* encodedPldmBlock = rdeCommand.data() + 102 sizeof(RdeOperationInitReqHeader) + 103 header->operationLocatorLength; 104 105 // Decoded the data. 106 if (decoder.decode(dictionaries, std::span(encodedPldmBlock, 107 header->requestPayloadLength)) != 108 0) 109 { 110 stdplus::print(stderr, "BEJ decoding failed.\n"); 111 return RdeDecodeStatus::RdeBejDecodingError; 112 } 113 114 // Post the output. 115 if (!exStorer->publishJson(decoder.getOutput())) 116 { 117 stdplus::print(stderr, "Failed to write to ExternalStorer.\n"); 118 return RdeDecodeStatus::RdeExternalStorerError; 119 } 120 return RdeDecodeStatus::RdeOk; 121 } 122 123 RdeDecodeStatus 124 RdeCommandHandler::multiPartReceiveResp(std::span<const uint8_t> rdeCommand) 125 { 126 const MultipartReceiveResHeader* header = 127 reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data()); 128 129 // This is a hack to get the resource ID for the dictionary data. Even 130 // though nextDataTransferHandle field is supposed to be used for something 131 // else, BIOS is using it to specify the resource ID corresponding to the 132 // dictionary data. 133 uint32_t resourceId = header->nextDataTransferHandle; 134 135 // data points to the payload of the MultipartReceive. 136 const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader); 137 RdeDecodeStatus ret = RdeDecodeStatus::RdeOk; 138 139 switch (header->transferFlag) 140 { 141 case static_cast<uint8_t>( 142 RdeMultiReceiveTransferFlag::RdeMRecFlagStart): 143 handleFlagStart(header, data, resourceId); 144 break; 145 case static_cast<uint8_t>( 146 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle): 147 ret = handleFlagMiddle(header, data, resourceId); 148 break; 149 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd): 150 ret = handleFlagEnd(rdeCommand, header, data, resourceId); 151 break; 152 case static_cast<uint8_t>( 153 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd): 154 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId); 155 break; 156 default: 157 stdplus::print(stderr, "Invalid transfer flag: {}\n", 158 header->transferFlag); 159 ret = RdeDecodeStatus::RdeInvalidCommand; 160 } 161 162 // If there is a failure, this assignment is not useful. So we can do it 163 // even if there is a failure. 164 prevDictResourceId = resourceId; 165 return ret; 166 } 167 168 void RdeCommandHandler::calcCrcTable() 169 { 170 for (uint32_t i = 0; i < UINT8_MAX + 1; ++i) 171 { 172 uint32_t rem = i; 173 for (uint8_t k = 0; k < 8; ++k) 174 { 175 rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1; 176 } 177 crcTable[i] = rem; 178 } 179 } 180 181 void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream) 182 { 183 for (uint32_t i = 0; i < stream.size_bytes(); ++i) 184 { 185 crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8); 186 } 187 } 188 189 uint32_t RdeCommandHandler::finalChecksum() 190 { 191 return (crc ^ 0xFFFFFFFF); 192 } 193 194 RdeDecodeStatus 195 RdeCommandHandler::handleCrc(std::span<const uint8_t> multiReceiveRespCmd) 196 { 197 const MultipartReceiveResHeader* header = 198 reinterpret_cast<const MultipartReceiveResHeader*>( 199 multiReceiveRespCmd.data()); 200 const uint8_t* checksumPtr = multiReceiveRespCmd.data() + 201 sizeof(MultipartReceiveResHeader) + 202 header->dataLengthBytes; 203 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) | 204 (checksumPtr[2] << 16) | (checksumPtr[3] << 24); 205 206 if (finalChecksum() != checksum) 207 { 208 stdplus::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n", 209 checksum, finalChecksum()); 210 dictionaryManager.invalidateDictionaries(); 211 return RdeDecodeStatus::RdeInvalidChecksum; 212 } 213 return RdeDecodeStatus::RdeOk; 214 } 215 216 void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header, 217 const uint8_t* data, 218 uint32_t resourceId) 219 { 220 // This is a beginning of a dictionary. Reset CRC. 221 crc = 0xFFFFFFFF; 222 std::span dataS(data, header->dataLengthBytes); 223 dictionaryManager.startDictionaryEntry(resourceId, dataS); 224 // Start checksum calculation only for the data portion. 225 updateCrc(dataS); 226 flagState = RdeDictTransferFlagState::RdeStateStartRecvd; 227 } 228 229 RdeDecodeStatus 230 RdeCommandHandler::handleFlagMiddle(const MultipartReceiveResHeader* header, 231 const uint8_t* data, 232 uint32_t resourceId) 233 { 234 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd) 235 { 236 stdplus::print( 237 stderr, 238 "Invalid dictionary packet order. Need start before middle.\n"); 239 return RdeDecodeStatus::RdeInvalidPktOrder; 240 } 241 242 std::span dataS(data, header->dataLengthBytes); 243 if (prevDictResourceId != resourceId) 244 { 245 // Start of a new dictionary. Mark previous dictionary as 246 // complete. 247 dictionaryManager.markDataComplete(prevDictResourceId); 248 dictionaryManager.startDictionaryEntry(resourceId, dataS); 249 } 250 else 251 { 252 // Not a new dictionary. Add the received data to the existing 253 // dictionary. 254 if (!dictionaryManager.addDictionaryData(resourceId, dataS)) 255 { 256 stdplus::print(stderr, 257 "Failed to add dictionary data: ResourceId: {}\n", 258 resourceId); 259 return RdeDecodeStatus::RdeDictionaryError; 260 } 261 } 262 // Continue checksum calculation only for the data portion. 263 updateCrc(dataS); 264 return RdeDecodeStatus::RdeOk; 265 } 266 267 RdeDecodeStatus 268 RdeCommandHandler::handleFlagEnd(std::span<const uint8_t> rdeCommand, 269 const MultipartReceiveResHeader* header, 270 const uint8_t* data, uint32_t resourceId) 271 { 272 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd) 273 { 274 stdplus::print( 275 stderr, 276 "Invalid dictionary packet order. Need start before middle.\n"); 277 return RdeDecodeStatus::RdeInvalidPktOrder; 278 } 279 flagState = RdeDictTransferFlagState::RdeStateIdle; 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 if (!dictionaryManager.addDictionaryData(resourceId, dataS)) 292 { 293 stdplus::print(stderr, 294 "Failed to add dictionary data: ResourceId: {}\n", 295 resourceId); 296 return RdeDecodeStatus::RdeDictionaryError; 297 } 298 } 299 dictionaryManager.markDataComplete(resourceId); 300 301 // Continue checksum calculation only for the data portion. At the end of 302 // data, we will have the DataIntegrityChecksum field. So omit that when 303 // calculating checksum. 304 updateCrc(dataS); 305 auto ret = handleCrc(rdeCommand); 306 if (ret != RdeDecodeStatus::RdeOk) 307 { 308 return ret; 309 } 310 return RdeDecodeStatus::RdeStopFlagReceived; 311 } 312 313 RdeDecodeStatus RdeCommandHandler::handleFlagStartAndEnd( 314 std::span<const uint8_t> rdeCommand, 315 const MultipartReceiveResHeader* header, const uint8_t* data, 316 uint32_t resourceId) 317 { 318 // This is a beginning of a dictionary. Reset CRC. 319 crc = 0xFFFFFFFF; 320 // This is a beginning and end of a dictionary. 321 dictionaryManager.startDictionaryEntry( 322 resourceId, std::span(data, header->dataLengthBytes)); 323 dictionaryManager.markDataComplete(resourceId); 324 flagState = RdeDictTransferFlagState::RdeStateIdle; 325 326 // Do checksum calculation only for the data portion. At the end of data, we 327 // will have the DataIntegrityChecksum field. So omit that when calculating 328 // checksum. 329 updateCrc(std::span(data, header->dataLengthBytes)); 330 331 auto ret = handleCrc(rdeCommand); 332 if (ret != RdeDecodeStatus::RdeOk) 333 { 334 return ret; 335 } 336 return RdeDecodeStatus::RdeStopFlagReceived; 337 } 338 339 } // namespace rde 340 } // namespace bios_bmc_smm_error_logger 341