1 #include <algorithm> 2 #include <map> 3 #include <numeric> 4 #include "ipmi_fru_info_area.hpp" 5 #include <phosphor-logging/elog.hpp> 6 namespace ipmi 7 { 8 namespace fru 9 { 10 using namespace phosphor::logging; 11 12 //Property variables 13 static constexpr auto partNumber = "PartNumber"; 14 static constexpr auto serialNumber = "SerialNumber"; 15 static constexpr auto manufacturer = "Manufacturer"; 16 static constexpr auto buildDate = "BuildDate"; 17 static constexpr auto model = "Model"; 18 static constexpr auto prettyName = "PrettyName"; 19 static constexpr auto version = "Version"; 20 21 //Board info areas 22 static constexpr auto board = "Board"; 23 static constexpr auto chassis = "Chassis"; 24 static constexpr auto product = "Product"; 25 26 static constexpr auto specVersion = 0x1; 27 static constexpr auto recordUnitOfMeasurment = 0x8; //size in bytes 28 static constexpr auto checksumSize = 0x1; //size in bytes 29 static constexpr auto recordNotPresent = 0x0; 30 static constexpr auto englishLanguageCode = 0x0; 31 static constexpr auto typeLengthByteNull = 0x0; 32 static constexpr auto endOfCustomFields = 0xC1; 33 static constexpr auto commonHeaderFormatSize = 0x8; //size in bytes 34 static constexpr auto manufacturingDateSize = 0x3; 35 static constexpr auto areaSizeOffset = 0x1; 36 37 /** 38 * @brief Format Beginning of Individual IPMI FRU Data Section 39 * 40 * @param[in] langCode Language code 41 * @param[in/out] data FRU area data 42 */ 43 void preFormatProcessing(bool langCode, FruAreaData& data) 44 { 45 //Add id for version of FRU Info Storage Spec used 46 data.emplace_back(specVersion); 47 48 //Add Data Size - 0 as a placeholder, can edit after the data is finalized 49 data.emplace_back(typeLengthByteNull); 50 51 if (langCode) 52 { 53 data.emplace_back(englishLanguageCode); 54 } 55 } 56 57 /** 58 * @brief Append checksum of the FRU area data 59 * 60 * @param[in/out] data FRU area data 61 */ 62 void appendDataChecksum(FruAreaData& data) 63 { 64 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0); 65 // Push the Zero checksum as the last byte of this data 66 // This appears to be a simple summation of all the bytes 67 data.emplace_back(-checksumVal); 68 } 69 70 /** 71 * @brief Append padding bytes for the FRU area data 72 * 73 * @param[in/out] data FRU area data 74 */ 75 void padData(FruAreaData& data) 76 { 77 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurment; 78 if (pad) 79 { 80 data.resize((data.size() + recordUnitOfMeasurment - pad)); 81 } 82 } 83 84 /** 85 * @brief Format End of Individual IPMI FRU Data Section 86 * 87 * @param[in/out] fruAreaData FRU area info data 88 */ 89 void postFormatProcessing(FruAreaData& data) 90 { 91 //This area needs to be padded to a multiple of 8 bytes (after checksum) 92 padData(data); 93 94 //Set size of data info area 95 data.at(areaSizeOffset) = (data.size() + checksumSize) / 96 (recordUnitOfMeasurment); 97 98 //Finally add board info checksum 99 appendDataChecksum(data); 100 } 101 102 /** 103 * @brief Read property value from inventory and append to the FRU area data 104 * 105 * @param[in] key key to search for in the property inventory data 106 * @param[in] propMap map of property values 107 * @param[in,out] data FRU area data to be appended 108 */ 109 void appendData(const Property& key, const PropertyMap& propMap, 110 FruAreaData& data) 111 { 112 auto iter = propMap.find(key); 113 if (iter != propMap.end()) 114 { 115 auto value = iter->second; 116 //If starts with 0x or 0X remove them 117 //ex: 0x123a just take 123a 118 if ((value.compare(0, 2, "0x")) == 0 || 119 (value.compare(0, 2, "0X") == 0)) 120 { 121 value.erase(0, 2); 122 } 123 data.emplace_back(value.length()); 124 std::copy(value.begin(), value.end(), std::back_inserter(data)); 125 } 126 else 127 { 128 //set 0 size 129 data.emplace_back(typeLengthByteNull); 130 } 131 } 132 133 134 /** 135 * @brief Appends Build Date 136 * 137 * @param[in] propMap map of property values 138 * @param[in/out] data FRU area to add the manfufacture date 139 */ 140 void appendMfgDate(const PropertyMap& propMap, FruAreaData& data) 141 { 142 //MFG Date/Time 143 auto iter = propMap.find(buildDate); 144 if (iter != propMap.end()) 145 { 146 auto& value = iter->second; 147 if (value.length() == manufacturingDateSize) 148 { 149 std::copy( 150 value.begin(), value.end(), std::back_inserter(data)); 151 return; 152 } 153 } 154 //Blank date 155 data.emplace_back(0); 156 data.emplace_back(0); 157 data.emplace_back(0); 158 } 159 160 /** 161 * @brief Builds a section of the common header 162 * 163 * @param[in] infoAreaSize size of the FRU area to write 164 * @param[in] offset Current offset for data in overall record 165 * @param[in/out] data Common Header section data container 166 */ 167 void buildCommonHeaderSection( 168 const uint32_t& infoAreaSize, uint32_t& offset, FruAreaData& data) 169 { 170 //Check if data for internal use section populated 171 if (infoAreaSize == 0) 172 { 173 //Indicate record not present 174 data.emplace_back(recordNotPresent); 175 } 176 else 177 { 178 //Place data to define offset to area data section 179 data.emplace_back((offset + commonHeaderFormatSize) 180 / recordUnitOfMeasurment); 181 offset += infoAreaSize; 182 } 183 } 184 185 /** 186 * @brief Builds the Chassis info area data section 187 * 188 * @param[in] propMap map of properties for chassis info area 189 * @return FruAreaData container with chassis info area 190 */ 191 FruAreaData buildChassisInfoArea(const PropertyMap& propMap) 192 { 193 FruAreaData fruAreaData; 194 if (!propMap.empty()) 195 { 196 //Set formatting data that goes at the beginning of the record 197 preFormatProcessing(false, fruAreaData); 198 199 //chassis type 200 fruAreaData.emplace_back(0); 201 202 //Chasiss part number, in config.yaml it is configured as model 203 appendData(model, propMap, fruAreaData); 204 205 //Board serial number 206 appendData(serialNumber, propMap, fruAreaData); 207 208 //Indicate End of Custom Fields 209 fruAreaData.emplace_back(endOfCustomFields); 210 211 //Complete record data formatting 212 postFormatProcessing(fruAreaData); 213 } 214 return fruAreaData; 215 } 216 217 /** 218 * @brief Builds the Board info area data section 219 * 220 * @param[in] propMap map of properties for board info area 221 * @return FruAreaData container with board info area 222 */ 223 FruAreaData buildBoardInfoArea(const PropertyMap& propMap) 224 { 225 FruAreaData fruAreaData; 226 if (!propMap.empty()) 227 { 228 preFormatProcessing(true, fruAreaData); 229 230 //Manufacturing date 231 appendMfgDate(propMap, fruAreaData); 232 233 //manufacturer 234 appendData(manufacturer, propMap, fruAreaData); 235 236 //Product name/Pretty name 237 appendData(prettyName, propMap, fruAreaData); 238 239 //Board serial number 240 appendData(serialNumber, propMap, fruAreaData); 241 242 //Board part number 243 appendData(partNumber, propMap, fruAreaData); 244 245 //FRU File ID - Empty 246 fruAreaData.emplace_back(typeLengthByteNull); 247 248 // Empty FRU File ID bytes 249 fruAreaData.emplace_back(recordNotPresent); 250 251 //End of custom fields 252 fruAreaData.emplace_back(endOfCustomFields); 253 254 postFormatProcessing(fruAreaData); 255 } 256 return fruAreaData; 257 } 258 259 /** 260 * @brief Builds the Product info area data section 261 * 262 * @param[in] propMap map of FRU properties for Board info area 263 * @return FruAreaData container with product info area data 264 */ 265 FruAreaData buildProductInfoArea(const PropertyMap& propMap) 266 { 267 FruAreaData fruAreaData; 268 if (!propMap.empty()) 269 { 270 //Set formatting data that goes at the beginning of the record 271 preFormatProcessing(true, fruAreaData); 272 273 //manufacturer 274 appendData(manufacturer, propMap, fruAreaData); 275 276 //Product name/Pretty name 277 appendData(prettyName, propMap, fruAreaData); 278 279 //Product part/model number 280 appendData(model, propMap, fruAreaData); 281 282 //Product version 283 appendData(version, propMap, fruAreaData); 284 285 //Serial Number 286 appendData(serialNumber, propMap, fruAreaData); 287 288 //Add Asset Tag 289 fruAreaData.emplace_back(recordNotPresent); 290 291 //FRU File ID - Empty 292 fruAreaData.emplace_back(typeLengthByteNull); 293 294 // Empty FRU File ID bytes 295 fruAreaData.emplace_back(recordNotPresent); 296 297 //End of custom fields 298 fruAreaData.emplace_back(endOfCustomFields); 299 300 postFormatProcessing(fruAreaData); 301 } 302 return fruAreaData; 303 } 304 305 FruAreaData buildFruAreaData(const FruInventoryData& inventory) 306 { 307 FruAreaData combFruArea; 308 //Now build common header with data for this FRU Inv Record 309 //Use this variable to increment size of header as we go along to determine 310 //offset for the subsequent area offsets 311 uint32_t curDataOffset = 0; 312 313 //First byte is id for version of FRU Info Storage Spec used 314 combFruArea.emplace_back(specVersion); 315 316 //2nd byte is offset to internal use data 317 combFruArea.emplace_back(recordNotPresent); 318 319 //3rd byte is offset to chassis data 320 FruAreaData chassisArea; 321 auto chassisIt = inventory.find(chassis); 322 if (chassisIt != inventory.end()) 323 { 324 chassisArea = std::move(buildChassisInfoArea(chassisIt->second)); 325 } 326 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea); 327 328 //4th byte is offset to board data 329 FruAreaData boardArea; 330 auto boardIt = inventory.find(board); 331 if (boardIt != inventory.end()) 332 { 333 boardArea = std::move(buildBoardInfoArea(boardIt->second)); 334 } 335 336 //5th byte is offset to product data 337 FruAreaData prodArea; 338 auto prodIt = inventory.find(product); 339 if (prodIt != inventory.end()) 340 { 341 prodArea = std::move(buildProductInfoArea(prodIt->second)); 342 } 343 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea); 344 345 //6th byte is offset to multirecord data 346 combFruArea.emplace_back(recordNotPresent); 347 348 //7th byte is PAD 349 padData(combFruArea); 350 351 //8th (Final byte of Header Format) is the checksum 352 appendDataChecksum(combFruArea); 353 354 //Combine everything into one full IPMI FRU specification Record 355 //add chassis use area data 356 combFruArea.insert( 357 combFruArea.end(), chassisArea.begin(), chassisArea.end()); 358 359 //add board area data 360 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end()); 361 362 //add product use area data 363 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end()); 364 365 return combFruArea; 366 } 367 368 } //fru 369 } //ipmi 370