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