1 #include "rde/rde_handler.hpp" 2 3 #include <fmt/format.h> 4 5 #include <iostream> 6 7 namespace bios_bmc_smm_error_logger 8 { 9 namespace rde 10 { 11 12 /** 13 * @brief CRC-32 divisor. 14 * 15 * This is equivalent to the one used by IEEE802.3. 16 */ 17 constexpr uint32_t crcDevisor = 0xedb88320; 18 19 RdeCommandHandler::RdeCommandHandler( 20 std::unique_ptr<ExternalStorerInterface> exStorer) : 21 flagState(RdeDictTransferFlagState::RdeStateIdle), 22 exStorer(std::move(exStorer)) 23 { 24 // Initialize CRC table. 25 calcCrcTable(); 26 } 27 28 RdeDecodeStatus 29 RdeCommandHandler::decodeRdeCommand(std::span<const uint8_t> rdeCommand, 30 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 fmt::print(stderr, "Invalid command type\n"); 42 return RdeDecodeStatus::RdeInvalidCommand; 43 } 44 45 uint32_t RdeCommandHandler::getDictionaryCount() 46 { 47 return dictionaryManager.getDictionaryCount(); 48 } 49 50 RdeDecodeStatus 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 61 if (header->operationType != 62 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate)) 63 { 64 fmt::print(stderr, "Operation not supported\n"); 65 return RdeDecodeStatus::RdeUnsupportedOperation; 66 } 67 68 // OperationInit payload overflows are not suported. 69 if (header->sendDataTransferHandle != 0) 70 { 71 fmt::print(stderr, "Payload should fit in within the request\n"); 72 return RdeDecodeStatus::RdePayloadOverflow; 73 } 74 75 auto schemaDictOrErr = dictionaryManager.getDictionary(header->resourceID); 76 if (!schemaDictOrErr) 77 { 78 fmt::print(stderr, "Schema Dictionary not found for resourceId: {}\n", 79 header->resourceID); 80 return RdeDecodeStatus::RdeNoDictionary; 81 } 82 83 auto annotationDictOrErr = dictionaryManager.getAnnotationDictionary(); 84 if (!annotationDictOrErr) 85 { 86 fmt::print(stderr, "Annotation dictionary not found\n"); 87 return RdeDecodeStatus::RdeNoDictionary; 88 } 89 90 BejDictionaries dictionaries = { 91 .schemaDictionary = (*schemaDictOrErr).data(), 92 .annotationDictionary = (*annotationDictOrErr).data(), 93 // We do not use the error dictionary. 94 .errorDictionary = nullptr, 95 }; 96 97 // Soon after header, we have bejLocator field. Then we have the encoded 98 // data. 99 const uint8_t* encodedPldmBlock = rdeCommand.data() + 100 sizeof(RdeOperationInitReqHeader) + 101 header->operationLocatorLength; 102 103 // Decoded the data. 104 if (decoder.decode(dictionaries, std::span(encodedPldmBlock, 105 header->requestPayloadLength)) != 106 0) 107 { 108 fmt::print(stderr, "BEJ decoding failed.\n"); 109 return RdeDecodeStatus::RdeBejDecodingError; 110 } 111 112 // Post the output. 113 if (!exStorer->publishJson(decoder.getOutput())) 114 { 115 fmt::print(stderr, "Failed to write to ExternalStorer.\n"); 116 return RdeDecodeStatus::RdeExternalStorerError; 117 } 118 return RdeDecodeStatus::RdeOk; 119 } 120 121 RdeDecodeStatus 122 RdeCommandHandler::multiPartReceiveResp(std::span<const uint8_t> rdeCommand) 123 { 124 const MultipartReceiveResHeader* header = 125 reinterpret_cast<const MultipartReceiveResHeader*>(rdeCommand.data()); 126 127 // This is a hack to get the resource ID for the dictionary data. Even 128 // though nextDataTransferHandle field is supposed to be used for something 129 // else, BIOS is using it to specify the resource ID corresponding to the 130 // dictionary data. 131 uint32_t resourceId = header->nextDataTransferHandle; 132 133 // data points to the payload of the MultipartReceive. 134 const uint8_t* data = rdeCommand.data() + sizeof(MultipartReceiveResHeader); 135 RdeDecodeStatus ret = RdeDecodeStatus::RdeOk; 136 137 switch (header->transferFlag) 138 { 139 case static_cast<uint8_t>( 140 RdeMultiReceiveTransferFlag::RdeMRecFlagStart): 141 handleFlagStart(header, data, resourceId); 142 break; 143 case static_cast<uint8_t>( 144 RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle): 145 ret = handleFlagMiddle(header, data, resourceId); 146 break; 147 case static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd): 148 ret = handleFlagEnd(rdeCommand, header, data, resourceId); 149 break; 150 case static_cast<uint8_t>( 151 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd): 152 ret = handleFlagStartAndEnd(rdeCommand, header, data, resourceId); 153 break; 154 default: 155 fmt::print(stderr, "Invalid transfer flag: {}\n", 156 header->transferFlag); 157 ret = RdeDecodeStatus::RdeInvalidCommand; 158 } 159 160 // If there is a failure, this assignment is not useful. So we can do it 161 // even if there is a failure. 162 prevDictResourceId = resourceId; 163 return ret; 164 } 165 166 void RdeCommandHandler::calcCrcTable() 167 { 168 for (uint32_t i = 0; i < UINT8_MAX + 1; ++i) 169 { 170 uint32_t rem = i; 171 for (uint8_t k = 0; k < 8; ++k) 172 { 173 rem = (rem & 1) ? (rem >> 1) ^ crcDevisor : rem >> 1; 174 } 175 crcTable[i] = rem; 176 } 177 } 178 179 void RdeCommandHandler::updateCrc(std::span<const uint8_t> stream) 180 { 181 for (uint32_t i = 0; i < stream.size_bytes(); ++i) 182 { 183 crc = crcTable[(crc ^ stream[i]) & 0xff] ^ (crc >> 8); 184 } 185 } 186 187 uint32_t RdeCommandHandler::finalChecksum() 188 { 189 return (crc ^ 0xFFFFFFFF); 190 } 191 192 RdeDecodeStatus 193 RdeCommandHandler::handleCrc(std::span<const uint8_t> multiReceiveRespCmd) 194 { 195 const MultipartReceiveResHeader* header = 196 reinterpret_cast<const MultipartReceiveResHeader*>( 197 multiReceiveRespCmd.data()); 198 const uint8_t* checksumPtr = multiReceiveRespCmd.data() + 199 sizeof(MultipartReceiveResHeader) + 200 header->dataLengthBytes; 201 uint32_t checksum = checksumPtr[0] | (checksumPtr[1] << 8) | 202 (checksumPtr[2] << 16) | (checksumPtr[3] << 24); 203 204 if (finalChecksum() != checksum) 205 { 206 fmt::print(stderr, "Checksum failed. Ex: {} Calculated: {}\n", checksum, 207 finalChecksum()); 208 dictionaryManager.invalidateDictionaries(); 209 return RdeDecodeStatus::RdeInvalidChecksum; 210 } 211 return RdeDecodeStatus::RdeOk; 212 } 213 214 void RdeCommandHandler::handleFlagStart(const MultipartReceiveResHeader* header, 215 const uint8_t* data, 216 uint32_t resourceId) 217 { 218 // This is a beginning of a dictionary. Reset CRC. 219 crc = 0xFFFFFFFF; 220 std::span dataS(data, header->dataLengthBytes); 221 dictionaryManager.startDictionaryEntry(resourceId, dataS); 222 // Start checksum calculation only for the data portion. 223 updateCrc(dataS); 224 flagState = RdeDictTransferFlagState::RdeStateStartRecvd; 225 } 226 227 RdeDecodeStatus 228 RdeCommandHandler::handleFlagMiddle(const MultipartReceiveResHeader* header, 229 const uint8_t* data, 230 uint32_t resourceId) 231 { 232 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd) 233 { 234 fmt::print( 235 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 { 254 fmt::print(stderr, 255 "Failed to add dictionary data: ResourceId: {}\n", 256 resourceId); 257 return RdeDecodeStatus::RdeDictionaryError; 258 } 259 } 260 // Continue checksum calculation only for the data portion. 261 updateCrc(dataS); 262 return RdeDecodeStatus::RdeOk; 263 } 264 265 RdeDecodeStatus 266 RdeCommandHandler::handleFlagEnd(std::span<const uint8_t> rdeCommand, 267 const MultipartReceiveResHeader* header, 268 const uint8_t* data, uint32_t resourceId) 269 { 270 if (flagState != RdeDictTransferFlagState::RdeStateStartRecvd) 271 { 272 fmt::print( 273 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 { 291 fmt::print(stderr, 292 "Failed to add dictionary data: ResourceId: {}\n", 293 resourceId); 294 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 311 RdeDecodeStatus 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 339