111b9c3b1SVijay Khemka /* 211b9c3b1SVijay Khemka * Copyright (c) 2018 Intel Corporation. 311b9c3b1SVijay Khemka * Copyright (c) 2018-present Facebook. 411b9c3b1SVijay Khemka * 511b9c3b1SVijay Khemka * Licensed under the Apache License, Version 2.0 (the "License"); 611b9c3b1SVijay Khemka * you may not use this file except in compliance with the License. 711b9c3b1SVijay Khemka * You may obtain a copy of the License at 811b9c3b1SVijay Khemka * 911b9c3b1SVijay Khemka * http://www.apache.org/licenses/LICENSE-2.0 1011b9c3b1SVijay Khemka * 1111b9c3b1SVijay Khemka * Unless required by applicable law or agreed to in writing, software 1211b9c3b1SVijay Khemka * distributed under the License is distributed on an "AS IS" BASIS, 1311b9c3b1SVijay Khemka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1411b9c3b1SVijay Khemka * See the License for the specific language governing permissions and 1511b9c3b1SVijay Khemka * limitations under the License. 1611b9c3b1SVijay Khemka */ 1711b9c3b1SVijay Khemka 1811b9c3b1SVijay Khemka #include <ipmid/api.hpp> 1911b9c3b1SVijay Khemka 2011b9c3b1SVijay Khemka #include <boost/algorithm/string/join.hpp> 2111b9c3b1SVijay Khemka #include <nlohmann/json.hpp> 2211b9c3b1SVijay Khemka #include <iostream> 2311b9c3b1SVijay Khemka #include <sstream> 2411b9c3b1SVijay Khemka #include <fstream> 2511b9c3b1SVijay Khemka #include <phosphor-logging/log.hpp> 2611b9c3b1SVijay Khemka #include <sdbusplus/message/types.hpp> 2711b9c3b1SVijay Khemka #include <sdbusplus/timer.hpp> 2811b9c3b1SVijay Khemka #include <storagecommands.hpp> 2911b9c3b1SVijay Khemka 3011b9c3b1SVijay Khemka //---------------------------------------------------------------------- 3111b9c3b1SVijay Khemka // Platform specific functions for storing app data 3211b9c3b1SVijay Khemka //---------------------------------------------------------------------- 3311b9c3b1SVijay Khemka 3411b9c3b1SVijay Khemka static void toHexStr(std::vector<uint8_t> &bytes, std::string &hexStr) 3511b9c3b1SVijay Khemka { 3611b9c3b1SVijay Khemka std::stringstream stream; 3711b9c3b1SVijay Khemka stream << std::hex << std::uppercase << std::setfill('0'); 3811b9c3b1SVijay Khemka for (const uint8_t byte : bytes) 3911b9c3b1SVijay Khemka { 4011b9c3b1SVijay Khemka stream << std::setw(2) << static_cast<int>(byte); 4111b9c3b1SVijay Khemka } 4211b9c3b1SVijay Khemka hexStr = stream.str(); 4311b9c3b1SVijay Khemka } 4411b9c3b1SVijay Khemka 4511b9c3b1SVijay Khemka static int fromHexStr(const std::string hexStr, std::vector<uint8_t> &data) 4611b9c3b1SVijay Khemka { 4711b9c3b1SVijay Khemka for (unsigned int i = 0; i < hexStr.size(); i += 2) 4811b9c3b1SVijay Khemka { 4911b9c3b1SVijay Khemka try 5011b9c3b1SVijay Khemka { 5111b9c3b1SVijay Khemka data.push_back(static_cast<uint8_t>( 5211b9c3b1SVijay Khemka std::stoul(hexStr.substr(i, 2), nullptr, 16))); 5311b9c3b1SVijay Khemka } 5411b9c3b1SVijay Khemka catch (std::invalid_argument &e) 5511b9c3b1SVijay Khemka { 5611b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 5711b9c3b1SVijay Khemka return -1; 5811b9c3b1SVijay Khemka } 5911b9c3b1SVijay Khemka catch (std::out_of_range &e) 6011b9c3b1SVijay Khemka { 6111b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 6211b9c3b1SVijay Khemka return -1; 6311b9c3b1SVijay Khemka } 6411b9c3b1SVijay Khemka } 6511b9c3b1SVijay Khemka return 0; 6611b9c3b1SVijay Khemka } 6711b9c3b1SVijay Khemka 6811b9c3b1SVijay Khemka namespace fb_oem::ipmi::sel 6911b9c3b1SVijay Khemka { 7011b9c3b1SVijay Khemka 7111b9c3b1SVijay Khemka class SELData 7211b9c3b1SVijay Khemka { 7311b9c3b1SVijay Khemka private: 7411b9c3b1SVijay Khemka nlohmann::json selDataObj; 7511b9c3b1SVijay Khemka 7611b9c3b1SVijay Khemka void flush() 7711b9c3b1SVijay Khemka { 7811b9c3b1SVijay Khemka std::ofstream file(SEL_JSON_DATA_FILE); 7911b9c3b1SVijay Khemka file << selDataObj; 8011b9c3b1SVijay Khemka file.close(); 8111b9c3b1SVijay Khemka } 8211b9c3b1SVijay Khemka 8311b9c3b1SVijay Khemka void init() 8411b9c3b1SVijay Khemka { 8511b9c3b1SVijay Khemka selDataObj[KEY_SEL_VER] = 0x51; 8611b9c3b1SVijay Khemka selDataObj[KEY_SEL_COUNT] = 0; 8711b9c3b1SVijay Khemka selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF; 8811b9c3b1SVijay Khemka selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF; 8911b9c3b1SVijay Khemka selDataObj[KEY_OPER_SUPP] = 0x02; 9011b9c3b1SVijay Khemka /* Spec indicates that more than 64kB is free */ 9111b9c3b1SVijay Khemka selDataObj[KEY_FREE_SPACE] = 0xFFFF; 9211b9c3b1SVijay Khemka } 9311b9c3b1SVijay Khemka 9411b9c3b1SVijay Khemka public: 9511b9c3b1SVijay Khemka SELData() 9611b9c3b1SVijay Khemka { 9711b9c3b1SVijay Khemka /* Get App data stored in json file */ 9811b9c3b1SVijay Khemka std::ifstream file(SEL_JSON_DATA_FILE); 9911b9c3b1SVijay Khemka if (file) 10011b9c3b1SVijay Khemka { 10111b9c3b1SVijay Khemka file >> selDataObj; 10211b9c3b1SVijay Khemka file.close(); 10311b9c3b1SVijay Khemka } 10411b9c3b1SVijay Khemka 10511b9c3b1SVijay Khemka /* Initialize SelData object if no entries. */ 10611b9c3b1SVijay Khemka if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end()) 10711b9c3b1SVijay Khemka { 10811b9c3b1SVijay Khemka init(); 10911b9c3b1SVijay Khemka } 11011b9c3b1SVijay Khemka } 11111b9c3b1SVijay Khemka 11211b9c3b1SVijay Khemka int clear() 11311b9c3b1SVijay Khemka { 11411b9c3b1SVijay Khemka /* Clear the complete Sel Json object */ 11511b9c3b1SVijay Khemka selDataObj.clear(); 11611b9c3b1SVijay Khemka /* Reinitialize it with basic data */ 11711b9c3b1SVijay Khemka init(); 11811b9c3b1SVijay Khemka /* Save the erase time */ 11911b9c3b1SVijay Khemka struct timespec selTime = {}; 12011b9c3b1SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 12111b9c3b1SVijay Khemka { 12211b9c3b1SVijay Khemka return -1; 12311b9c3b1SVijay Khemka } 12411b9c3b1SVijay Khemka selDataObj[KEY_ERASE_TIME] = selTime.tv_sec; 12511b9c3b1SVijay Khemka flush(); 12611b9c3b1SVijay Khemka return 0; 12711b9c3b1SVijay Khemka } 12811b9c3b1SVijay Khemka 12911b9c3b1SVijay Khemka uint32_t getCount() 13011b9c3b1SVijay Khemka { 13111b9c3b1SVijay Khemka return selDataObj[KEY_SEL_COUNT]; 13211b9c3b1SVijay Khemka } 13311b9c3b1SVijay Khemka 13411b9c3b1SVijay Khemka void getInfo(GetSELInfoData &info) 13511b9c3b1SVijay Khemka { 13611b9c3b1SVijay Khemka info.selVersion = selDataObj[KEY_SEL_VER]; 13711b9c3b1SVijay Khemka info.entries = selDataObj[KEY_SEL_COUNT]; 13811b9c3b1SVijay Khemka info.freeSpace = selDataObj[KEY_FREE_SPACE]; 13911b9c3b1SVijay Khemka info.addTimeStamp = selDataObj[KEY_ADD_TIME]; 14011b9c3b1SVijay Khemka info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME]; 14111b9c3b1SVijay Khemka info.operationSupport = selDataObj[KEY_OPER_SUPP]; 14211b9c3b1SVijay Khemka } 14311b9c3b1SVijay Khemka 14411b9c3b1SVijay Khemka int getEntry(uint32_t index, std::string &rawStr) 14511b9c3b1SVijay Khemka { 14611b9c3b1SVijay Khemka std::stringstream ss; 14711b9c3b1SVijay Khemka ss << std::hex; 14811b9c3b1SVijay Khemka ss << std::setw(2) << std::setfill('0') << index; 14911b9c3b1SVijay Khemka 15011b9c3b1SVijay Khemka /* Check or the requested SEL Entry, if record is available */ 15111b9c3b1SVijay Khemka if (selDataObj.find(ss.str()) == selDataObj.end()) 15211b9c3b1SVijay Khemka { 15311b9c3b1SVijay Khemka return -1; 15411b9c3b1SVijay Khemka } 15511b9c3b1SVijay Khemka 15611b9c3b1SVijay Khemka rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW]; 15711b9c3b1SVijay Khemka return 0; 15811b9c3b1SVijay Khemka } 15911b9c3b1SVijay Khemka 16011b9c3b1SVijay Khemka int addEntry(std::string keyStr) 16111b9c3b1SVijay Khemka { 16211b9c3b1SVijay Khemka struct timespec selTime = {}; 16311b9c3b1SVijay Khemka 16411b9c3b1SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 16511b9c3b1SVijay Khemka { 16611b9c3b1SVijay Khemka return -1; 16711b9c3b1SVijay Khemka } 16811b9c3b1SVijay Khemka 16911b9c3b1SVijay Khemka selDataObj[KEY_ADD_TIME] = selTime.tv_sec; 17011b9c3b1SVijay Khemka 17111b9c3b1SVijay Khemka int selCount = selDataObj[KEY_SEL_COUNT]; 17211b9c3b1SVijay Khemka selDataObj[KEY_SEL_COUNT] = ++selCount; 17311b9c3b1SVijay Khemka 17411b9c3b1SVijay Khemka std::stringstream ss; 17511b9c3b1SVijay Khemka ss << std::hex; 17611b9c3b1SVijay Khemka ss << std::setw(2) << std::setfill('0') << selCount; 17711b9c3b1SVijay Khemka 17811b9c3b1SVijay Khemka selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr; 17911b9c3b1SVijay Khemka flush(); 18011b9c3b1SVijay Khemka return selCount; 18111b9c3b1SVijay Khemka } 18211b9c3b1SVijay Khemka }; 18311b9c3b1SVijay Khemka 184f36f345fSVijay Khemka static void parseStdSel(StdSELEntry *data, std::string &errStr) 185f36f345fSVijay Khemka { 186f36f345fSVijay Khemka std::stringstream tmpStream; 187f36f345fSVijay Khemka tmpStream << std::hex << std::uppercase; 188f36f345fSVijay Khemka 189f36f345fSVijay Khemka /* TODO: add pal_add_cri_sel */ 190f36f345fSVijay Khemka switch (data->sensorNum) 191f36f345fSVijay Khemka { 192f36f345fSVijay Khemka case memoryEccError: 193f36f345fSVijay Khemka switch (data->eventData1 & 0x0F) 194f36f345fSVijay Khemka { 195f36f345fSVijay Khemka case 0x00: 196f36f345fSVijay Khemka errStr = "Correctable"; 197f36f345fSVijay Khemka tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 198f36f345fSVijay Khemka << data->eventData3 << " ECC err"; 199f36f345fSVijay Khemka break; 200f36f345fSVijay Khemka case 0x01: 201f36f345fSVijay Khemka errStr = "Uncorrectable"; 202f36f345fSVijay Khemka tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 203f36f345fSVijay Khemka << data->eventData3 << " UECC err"; 204f36f345fSVijay Khemka break; 205f36f345fSVijay Khemka case 0x02: 206f36f345fSVijay Khemka errStr = "Parity"; 207f36f345fSVijay Khemka break; 208f36f345fSVijay Khemka case 0x05: 209f36f345fSVijay Khemka errStr = "Correctable ECC error Logging Limit Reached"; 210f36f345fSVijay Khemka break; 211f36f345fSVijay Khemka default: 212f36f345fSVijay Khemka errStr = "Unknown"; 213f36f345fSVijay Khemka } 214f36f345fSVijay Khemka break; 215f36f345fSVijay Khemka case memoryErrLogDIS: 216f36f345fSVijay Khemka if ((data->eventData1 & 0x0F) == 0) 217f36f345fSVijay Khemka { 218f36f345fSVijay Khemka errStr = "Correctable Memory Error Logging Disabled"; 219f36f345fSVijay Khemka } 220f36f345fSVijay Khemka else 221f36f345fSVijay Khemka { 222f36f345fSVijay Khemka errStr = "Unknown"; 223f36f345fSVijay Khemka } 224f36f345fSVijay Khemka break; 225f36f345fSVijay Khemka default: 226f36f345fSVijay Khemka 227f36f345fSVijay Khemka /* TODO: parse sel helper */ 228f36f345fSVijay Khemka errStr = "Unknown"; 229f36f345fSVijay Khemka return; 230f36f345fSVijay Khemka } 231f36f345fSVijay Khemka 232f36f345fSVijay Khemka errStr += " (DIMM " + std::to_string(data->eventData3) + ")"; 233f36f345fSVijay Khemka errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03); 234f36f345fSVijay Khemka 235f36f345fSVijay Khemka switch ((data->eventData2 & 0x0C) >> 2) 236f36f345fSVijay Khemka { 237f36f345fSVijay Khemka case 0x00: 238f36f345fSVijay Khemka // Ignore when " All info available" 239f36f345fSVijay Khemka break; 240f36f345fSVijay Khemka case 0x01: 241f36f345fSVijay Khemka errStr += " DIMM info not valid"; 242f36f345fSVijay Khemka break; 243f36f345fSVijay Khemka case 0x02: 244f36f345fSVijay Khemka errStr += " CHN info not valid"; 245f36f345fSVijay Khemka break; 246f36f345fSVijay Khemka case 0x03: 247f36f345fSVijay Khemka errStr += " CPU info not valid"; 248f36f345fSVijay Khemka break; 249f36f345fSVijay Khemka default: 250f36f345fSVijay Khemka errStr += " Unknown"; 251f36f345fSVijay Khemka } 252f36f345fSVijay Khemka 253f36f345fSVijay Khemka if (((data->eventType & 0x80) >> 7) == 0) 254f36f345fSVijay Khemka { 255f36f345fSVijay Khemka errStr += " Assertion"; 256f36f345fSVijay Khemka } 257f36f345fSVijay Khemka else 258f36f345fSVijay Khemka { 259f36f345fSVijay Khemka errStr += " Deassertion"; 260f36f345fSVijay Khemka } 261f36f345fSVijay Khemka 262f36f345fSVijay Khemka return; 263f36f345fSVijay Khemka } 264f36f345fSVijay Khemka 265f36f345fSVijay Khemka static void parseOemSel(TsOemSELEntry *data, std::string &errStr) 266f36f345fSVijay Khemka { 267f36f345fSVijay Khemka std::stringstream tmpStream; 268f36f345fSVijay Khemka tmpStream << std::hex << std::uppercase << std::setfill('0'); 269f36f345fSVijay Khemka 270f36f345fSVijay Khemka switch (data->recordType) 271f36f345fSVijay Khemka { 272f36f345fSVijay Khemka case 0xC0: 273f36f345fSVijay Khemka tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1] 274f36f345fSVijay Khemka << std::setw(2) << (int)data->oemData[0] << " DID:0x" 275f36f345fSVijay Khemka << std::setw(2) << (int)data->oemData[3] << std::setw(2) 276f36f345fSVijay Khemka << (int)data->oemData[2] << " Slot:0x" << std::setw(2) 277f36f345fSVijay Khemka << (int)data->oemData[4] << " Error ID:0x" << std::setw(2) 278f36f345fSVijay Khemka << (int)data->oemData[5]; 279f36f345fSVijay Khemka break; 280f36f345fSVijay Khemka case 0xC2: 281f36f345fSVijay Khemka tmpStream << "Extra info:0x" << std::setw(2) 282f36f345fSVijay Khemka << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2) 283f36f345fSVijay Khemka << (int)data->oemData[3] << std::setw(2) 284f36f345fSVijay Khemka << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2) 285f36f345fSVijay Khemka << (int)data->oemData[5] << std::setw(2) 286f36f345fSVijay Khemka << (int)data->oemData[4]; 287f36f345fSVijay Khemka break; 288f36f345fSVijay Khemka case 0xC3: 289f36f345fSVijay Khemka int bank = (data->oemData[1] & 0xf0) >> 4; 290f36f345fSVijay Khemka int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2]; 291f36f345fSVijay Khemka 292f36f345fSVijay Khemka tmpStream << "Fail Device:0x" << std::setw(2) 293f36f345fSVijay Khemka << (int)data->oemData[0] << " Bank:0x" << std::setw(2) 294f36f345fSVijay Khemka << bank << " Column:0x" << std::setw(2) << col 295f36f345fSVijay Khemka << " Failed Row:0x" << std::setw(2) 296f36f345fSVijay Khemka << (int)data->oemData[3] << std::setw(2) 297f36f345fSVijay Khemka << (int)data->oemData[4] << std::setw(2) 298f36f345fSVijay Khemka << (int)data->oemData[5]; 299f36f345fSVijay Khemka } 300f36f345fSVijay Khemka 301f36f345fSVijay Khemka errStr = tmpStream.str(); 302f36f345fSVijay Khemka 303f36f345fSVijay Khemka return; 304f36f345fSVijay Khemka } 305f36f345fSVijay Khemka 306*34a875f3SVijay Khemka static void parseOemUnifiedSel(NtsOemSELEntry *data, std::string &errStr) 307*34a875f3SVijay Khemka { 308*34a875f3SVijay Khemka uint8_t *ptr = data->oemData; 309*34a875f3SVijay Khemka int genInfo = ptr[0]; 310*34a875f3SVijay Khemka int errType = genInfo & 0x0f; 311*34a875f3SVijay Khemka std::vector<std::string> dimmEvent = { 312*34a875f3SVijay Khemka "Memory training failure", "Memory correctable error", 313*34a875f3SVijay Khemka "Memory uncorrectable error", "Reserved"}; 314*34a875f3SVijay Khemka 315*34a875f3SVijay Khemka std::stringstream tmpStream; 316*34a875f3SVijay Khemka tmpStream << std::hex << std::uppercase << std::setfill('0'); 317*34a875f3SVijay Khemka 318*34a875f3SVijay Khemka switch (errType) 319*34a875f3SVijay Khemka { 320*34a875f3SVijay Khemka case unifiedPcieErr: 321*34a875f3SVijay Khemka if (((genInfo & 0x10) >> 4) == 0) // x86 322*34a875f3SVijay Khemka { 323*34a875f3SVijay Khemka tmpStream << "GeneralInfo: x86/PCIeErr(0x" << std::setw(2) 324*34a875f3SVijay Khemka << genInfo << "),"; 325*34a875f3SVijay Khemka } 326*34a875f3SVijay Khemka 327*34a875f3SVijay Khemka tmpStream << " Bus " << std::setw(2) << (int)(ptr[8]) << "/Dev " 328*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[7] >> 3) << "/Fun " 329*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[7] & 0x7) 330*34a875f3SVijay Khemka << ", TotalErrID1Cnt: 0x" << std::setw(4) 331*34a875f3SVijay Khemka << (int)((ptr[10] << 8) | ptr[9]) << ", ErrID2: 0x" 332*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[11]) << ", ErrID1: 0x" 333*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[12]); 334*34a875f3SVijay Khemka 335*34a875f3SVijay Khemka break; 336*34a875f3SVijay Khemka case unifiedMemErr: 337*34a875f3SVijay Khemka tmpStream << "GeneralInfo: MemErr(0x" << std::setw(2) << genInfo 338*34a875f3SVijay Khemka << "), DIMM Slot Location: Sled " << std::setw(2) 339*34a875f3SVijay Khemka << (int)((ptr[5] >> 4) & 0x03) << "/Socket " 340*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[5] & 0x0f) << ", Channel " 341*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[6] & 0x0f) << ", Slot " 342*34a875f3SVijay Khemka << std::setw(2) << (int)(ptr[7] & 0x0f) 343*34a875f3SVijay Khemka << ", DIMM Failure Event: " << dimmEvent[(ptr[9] & 0x03)] 344*34a875f3SVijay Khemka << ", Major Code: 0x" << std::setw(2) << (int)(ptr[10]) 345*34a875f3SVijay Khemka << ", Minor Code: 0x" << std::setw(2) << (int)(ptr[11]); 346*34a875f3SVijay Khemka 347*34a875f3SVijay Khemka break; 348*34a875f3SVijay Khemka default: 349*34a875f3SVijay Khemka std::vector<uint8_t> oemData(ptr, ptr + 13); 350*34a875f3SVijay Khemka std::string oemDataStr; 351*34a875f3SVijay Khemka toHexStr(oemData, oemDataStr); 352*34a875f3SVijay Khemka tmpStream << "Undefined Error Type(0x" << std::setw(2) << errType 353*34a875f3SVijay Khemka << "), Raw: " << oemDataStr; 354*34a875f3SVijay Khemka } 355*34a875f3SVijay Khemka 356*34a875f3SVijay Khemka errStr = tmpStream.str(); 357*34a875f3SVijay Khemka 358*34a875f3SVijay Khemka return; 359*34a875f3SVijay Khemka } 360*34a875f3SVijay Khemka 361f36f345fSVijay Khemka static void parseSelData(std::vector<uint8_t> &reqData, std::string &msgLog) 362f36f345fSVijay Khemka { 363f36f345fSVijay Khemka 364f36f345fSVijay Khemka /* Get record type */ 365f36f345fSVijay Khemka int recType = reqData[2]; 366f36f345fSVijay Khemka std::string errType, errLog; 367f36f345fSVijay Khemka 368f36f345fSVijay Khemka uint8_t *ptr = NULL; 369f36f345fSVijay Khemka 370f36f345fSVijay Khemka std::stringstream recTypeStream; 371f36f345fSVijay Khemka recTypeStream << std::hex << std::uppercase << std::setfill('0') 372f36f345fSVijay Khemka << std::setw(2) << recType; 373f36f345fSVijay Khemka 374f36f345fSVijay Khemka msgLog = "SEL Entry: FRU: 1, Record: "; 375f36f345fSVijay Khemka 376f36f345fSVijay Khemka if (recType == stdErrType) 377f36f345fSVijay Khemka { 378f36f345fSVijay Khemka StdSELEntry *data = reinterpret_cast<StdSELEntry *>(&reqData[0]); 379f36f345fSVijay Khemka std::string sensorName; 380f36f345fSVijay Khemka 381f36f345fSVijay Khemka errType = stdErr; 382f36f345fSVijay Khemka if (data->sensorType == 0x1F) 383f36f345fSVijay Khemka { 384f36f345fSVijay Khemka sensorName = "OS"; 385f36f345fSVijay Khemka } 386f36f345fSVijay Khemka else 387f36f345fSVijay Khemka { 388f36f345fSVijay Khemka auto findSensorName = sensorNameTable.find(data->sensorNum); 389f36f345fSVijay Khemka if (findSensorName == sensorNameTable.end()) 390f36f345fSVijay Khemka { 391f36f345fSVijay Khemka sensorName = "Unknown"; 392f36f345fSVijay Khemka } 393f36f345fSVijay Khemka else 394f36f345fSVijay Khemka { 395f36f345fSVijay Khemka sensorName = findSensorName->second; 396f36f345fSVijay Khemka } 397f36f345fSVijay Khemka } 398f36f345fSVijay Khemka 399f36f345fSVijay Khemka std::tm *ts = localtime((time_t *)(&(data->timeStamp))); 400f36f345fSVijay Khemka std::string timeStr = std::asctime(ts); 401f36f345fSVijay Khemka 402f36f345fSVijay Khemka parseStdSel(data, errLog); 403f36f345fSVijay Khemka ptr = &(data->eventData1); 404f36f345fSVijay Khemka std::vector<uint8_t> evtData(ptr, ptr + 3); 405f36f345fSVijay Khemka std::string eventData; 406f36f345fSVijay Khemka toHexStr(evtData, eventData); 407f36f345fSVijay Khemka 408f36f345fSVijay Khemka std::stringstream senNumStream; 409f36f345fSVijay Khemka senNumStream << std::hex << std::uppercase << std::setfill('0') 410f36f345fSVijay Khemka << std::setw(2) << (int)(data->sensorNum); 411f36f345fSVijay Khemka 412f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + 413f36f345fSVijay Khemka "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" + 414f36f345fSVijay Khemka senNumStream.str() + "), Event Data: (" + eventData + ") " + 415f36f345fSVijay Khemka errLog; 416f36f345fSVijay Khemka } 417f36f345fSVijay Khemka else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax)) 418f36f345fSVijay Khemka { 419f36f345fSVijay Khemka /* timestamped OEM SEL records */ 420f36f345fSVijay Khemka TsOemSELEntry *data = reinterpret_cast<TsOemSELEntry *>(&reqData[0]); 421f36f345fSVijay Khemka ptr = data->mfrId; 422f36f345fSVijay Khemka std::vector<uint8_t> mfrIdData(ptr, ptr + 3); 423f36f345fSVijay Khemka std::string mfrIdStr; 424f36f345fSVijay Khemka toHexStr(mfrIdData, mfrIdStr); 425f36f345fSVijay Khemka 426f36f345fSVijay Khemka ptr = data->oemData; 427f36f345fSVijay Khemka std::vector<uint8_t> oemData(ptr, ptr + 6); 428f36f345fSVijay Khemka std::string oemDataStr; 429f36f345fSVijay Khemka toHexStr(oemData, oemDataStr); 430f36f345fSVijay Khemka 431f36f345fSVijay Khemka std::tm *ts = localtime((time_t *)(&(data->timeStamp))); 432f36f345fSVijay Khemka std::string timeStr = std::asctime(ts); 433f36f345fSVijay Khemka 434f36f345fSVijay Khemka errType = oemTSErr; 435f36f345fSVijay Khemka parseOemSel(data, errLog); 436f36f345fSVijay Khemka 437f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + 438f36f345fSVijay Khemka "), Time: " + timeStr + ", MFG ID: " + mfrIdStr + 439f36f345fSVijay Khemka ", OEM Data: (" + oemDataStr + ") " + errLog; 440f36f345fSVijay Khemka } 441*34a875f3SVijay Khemka else if (recType == fbUniErrType) 442*34a875f3SVijay Khemka { 443*34a875f3SVijay Khemka NtsOemSELEntry *data = reinterpret_cast<NtsOemSELEntry *>(&reqData[0]); 444*34a875f3SVijay Khemka errType = fbUniSELErr; 445*34a875f3SVijay Khemka parseOemUnifiedSel(data, errLog); 446*34a875f3SVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog; 447*34a875f3SVijay Khemka } 448f36f345fSVijay Khemka else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax)) 449f36f345fSVijay Khemka { 450f36f345fSVijay Khemka /* Non timestamped OEM SEL records */ 451f36f345fSVijay Khemka NtsOemSELEntry *data = reinterpret_cast<NtsOemSELEntry *>(&reqData[0]); 452f36f345fSVijay Khemka errType = oemNTSErr; 453f36f345fSVijay Khemka 454f36f345fSVijay Khemka ptr = data->oemData; 455f36f345fSVijay Khemka std::vector<uint8_t> oemData(ptr, ptr + 13); 456f36f345fSVijay Khemka std::string oemDataStr; 457f36f345fSVijay Khemka toHexStr(oemData, oemDataStr); 458f36f345fSVijay Khemka 459f36f345fSVijay Khemka parseOemSel((TsOemSELEntry *)data, errLog); 460f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" + 461f36f345fSVijay Khemka oemDataStr + ") " + errLog; 462f36f345fSVijay Khemka } 463f36f345fSVijay Khemka else 464f36f345fSVijay Khemka { 465f36f345fSVijay Khemka errType = unknownErr; 466f36f345fSVijay Khemka toHexStr(reqData, errLog); 467f36f345fSVijay Khemka msgLog += 468f36f345fSVijay Khemka errType + " (0x" + recTypeStream.str() + ") RawData: " + errLog; 469f36f345fSVijay Khemka } 470f36f345fSVijay Khemka } 471f36f345fSVijay Khemka 47211b9c3b1SVijay Khemka } // namespace fb_oem::ipmi::sel 47311b9c3b1SVijay Khemka 47411b9c3b1SVijay Khemka namespace ipmi 47511b9c3b1SVijay Khemka { 47611b9c3b1SVijay Khemka 47711b9c3b1SVijay Khemka namespace storage 47811b9c3b1SVijay Khemka { 47911b9c3b1SVijay Khemka 48011b9c3b1SVijay Khemka static void registerSELFunctions() __attribute__((constructor)); 48111b9c3b1SVijay Khemka static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101))); 48211b9c3b1SVijay Khemka 48311b9c3b1SVijay Khemka ipmi::RspType<uint8_t, // SEL version 48411b9c3b1SVijay Khemka uint16_t, // SEL entry count 48511b9c3b1SVijay Khemka uint16_t, // free space 48611b9c3b1SVijay Khemka uint32_t, // last add timestamp 48711b9c3b1SVijay Khemka uint32_t, // last erase timestamp 48811b9c3b1SVijay Khemka uint8_t> // operation support 48911b9c3b1SVijay Khemka ipmiStorageGetSELInfo() 49011b9c3b1SVijay Khemka { 49111b9c3b1SVijay Khemka 49211b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELInfoData info; 49311b9c3b1SVijay Khemka 49411b9c3b1SVijay Khemka selObj.getInfo(info); 49511b9c3b1SVijay Khemka return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace, 49611b9c3b1SVijay Khemka info.addTimeStamp, info.eraseTimeStamp, 49711b9c3b1SVijay Khemka info.operationSupport); 49811b9c3b1SVijay Khemka } 49911b9c3b1SVijay Khemka 50011b9c3b1SVijay Khemka ipmi::RspType<uint16_t, std::vector<uint8_t>> 50111b9c3b1SVijay Khemka ipmiStorageGetSELEntry(std::vector<uint8_t> data) 50211b9c3b1SVijay Khemka { 50311b9c3b1SVijay Khemka 50411b9c3b1SVijay Khemka if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest)) 50511b9c3b1SVijay Khemka { 50611b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 50711b9c3b1SVijay Khemka } 50811b9c3b1SVijay Khemka 50911b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELEntryRequest *reqData = 51011b9c3b1SVijay Khemka reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest *>(&data[0]); 51111b9c3b1SVijay Khemka 51211b9c3b1SVijay Khemka if (reqData->reservID != 0) 51311b9c3b1SVijay Khemka { 51411b9c3b1SVijay Khemka if (!checkSELReservation(reqData->reservID)) 51511b9c3b1SVijay Khemka { 51611b9c3b1SVijay Khemka return ipmi::responseInvalidReservationId(); 51711b9c3b1SVijay Khemka } 51811b9c3b1SVijay Khemka } 51911b9c3b1SVijay Khemka 52011b9c3b1SVijay Khemka uint16_t selCnt = selObj.getCount(); 52111b9c3b1SVijay Khemka if (selCnt == 0) 52211b9c3b1SVijay Khemka { 52311b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 52411b9c3b1SVijay Khemka } 52511b9c3b1SVijay Khemka 52611b9c3b1SVijay Khemka /* If it is asked for first entry */ 52711b9c3b1SVijay Khemka if (reqData->recordID == fb_oem::ipmi::sel::firstEntry) 52811b9c3b1SVijay Khemka { 52911b9c3b1SVijay Khemka /* First Entry (0x0000) as per Spec */ 53011b9c3b1SVijay Khemka reqData->recordID = 1; 53111b9c3b1SVijay Khemka } 53211b9c3b1SVijay Khemka else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry) 53311b9c3b1SVijay Khemka { 53411b9c3b1SVijay Khemka /* Last entry (0xFFFF) as per Spec */ 53511b9c3b1SVijay Khemka reqData->recordID = selCnt; 53611b9c3b1SVijay Khemka } 53711b9c3b1SVijay Khemka 53811b9c3b1SVijay Khemka std::string ipmiRaw; 53911b9c3b1SVijay Khemka 54011b9c3b1SVijay Khemka if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0) 54111b9c3b1SVijay Khemka { 54211b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 54311b9c3b1SVijay Khemka } 54411b9c3b1SVijay Khemka 54511b9c3b1SVijay Khemka std::vector<uint8_t> recDataBytes; 54611b9c3b1SVijay Khemka if (fromHexStr(ipmiRaw, recDataBytes) < 0) 54711b9c3b1SVijay Khemka { 54811b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 54911b9c3b1SVijay Khemka } 55011b9c3b1SVijay Khemka 55111b9c3b1SVijay Khemka /* Identify the next SEL record ID. If recordID is same as 55211b9c3b1SVijay Khemka * total SeL count then next id should be last entry else 55311b9c3b1SVijay Khemka * it should be incremented by 1 to current RecordID 55411b9c3b1SVijay Khemka */ 55511b9c3b1SVijay Khemka uint16_t nextRecord; 55611b9c3b1SVijay Khemka if (reqData->recordID == selCnt) 55711b9c3b1SVijay Khemka { 55811b9c3b1SVijay Khemka nextRecord = fb_oem::ipmi::sel::lastEntry; 55911b9c3b1SVijay Khemka } 56011b9c3b1SVijay Khemka else 56111b9c3b1SVijay Khemka { 56211b9c3b1SVijay Khemka nextRecord = reqData->recordID + 1; 56311b9c3b1SVijay Khemka } 56411b9c3b1SVijay Khemka 56511b9c3b1SVijay Khemka if (reqData->readLen == fb_oem::ipmi::sel::entireRecord) 56611b9c3b1SVijay Khemka { 56711b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recDataBytes); 56811b9c3b1SVijay Khemka } 56911b9c3b1SVijay Khemka else 57011b9c3b1SVijay Khemka { 57111b9c3b1SVijay Khemka if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize || 57211b9c3b1SVijay Khemka reqData->readLen > fb_oem::ipmi::sel::selRecordSize) 57311b9c3b1SVijay Khemka { 57411b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 57511b9c3b1SVijay Khemka } 57611b9c3b1SVijay Khemka std::vector<uint8_t> recPartData; 57711b9c3b1SVijay Khemka 57811b9c3b1SVijay Khemka auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset; 57911b9c3b1SVijay Khemka auto readLength = std::min(diff, static_cast<int>(reqData->readLen)); 58011b9c3b1SVijay Khemka 58111b9c3b1SVijay Khemka for (int i = 0; i < readLength; i++) 58211b9c3b1SVijay Khemka { 58311b9c3b1SVijay Khemka recPartData.push_back(recDataBytes[i + reqData->offset]); 58411b9c3b1SVijay Khemka } 58511b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recPartData); 58611b9c3b1SVijay Khemka } 58711b9c3b1SVijay Khemka } 58811b9c3b1SVijay Khemka 58911b9c3b1SVijay Khemka ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(std::vector<uint8_t> data) 59011b9c3b1SVijay Khemka { 59111b9c3b1SVijay Khemka /* Per the IPMI spec, need to cancel any reservation when a 59211b9c3b1SVijay Khemka * SEL entry is added 59311b9c3b1SVijay Khemka */ 59411b9c3b1SVijay Khemka cancelSELReservation(); 59511b9c3b1SVijay Khemka 59611b9c3b1SVijay Khemka if (data.size() != fb_oem::ipmi::sel::selRecordSize) 59711b9c3b1SVijay Khemka { 59811b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 59911b9c3b1SVijay Khemka } 60011b9c3b1SVijay Khemka 60111b9c3b1SVijay Khemka std::string ipmiRaw, logErr; 60211b9c3b1SVijay Khemka toHexStr(data, ipmiRaw); 60311b9c3b1SVijay Khemka 604f36f345fSVijay Khemka /* Parse sel data and get an error log to be filed */ 605f36f345fSVijay Khemka fb_oem::ipmi::sel::parseSelData(data, logErr); 606f36f345fSVijay Khemka 60711b9c3b1SVijay Khemka /* Log the Raw SEL message to the journal */ 60811b9c3b1SVijay Khemka std::string journalMsg = "SEL Entry Added: " + ipmiRaw; 609f36f345fSVijay Khemka 61011b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(journalMsg.c_str()); 611f36f345fSVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(logErr.c_str()); 61211b9c3b1SVijay Khemka 61311b9c3b1SVijay Khemka int responseID = selObj.addEntry(ipmiRaw.c_str()); 61411b9c3b1SVijay Khemka if (responseID < 0) 61511b9c3b1SVijay Khemka { 61611b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 61711b9c3b1SVijay Khemka } 61811b9c3b1SVijay Khemka return ipmi::responseSuccess((uint16_t)responseID); 61911b9c3b1SVijay Khemka } 62011b9c3b1SVijay Khemka 621c1921c63SVijay Khemka ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID, 622c1921c63SVijay Khemka const std::array<uint8_t, 3> &clr, 623c1921c63SVijay Khemka uint8_t eraseOperation) 624c1921c63SVijay Khemka { 625c1921c63SVijay Khemka if (!checkSELReservation(reservationID)) 626c1921c63SVijay Khemka { 627c1921c63SVijay Khemka return ipmi::responseInvalidReservationId(); 628c1921c63SVijay Khemka } 629c1921c63SVijay Khemka 630c1921c63SVijay Khemka static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 631c1921c63SVijay Khemka if (clr != clrExpected) 632c1921c63SVijay Khemka { 633c1921c63SVijay Khemka return ipmi::responseInvalidFieldRequest(); 634c1921c63SVijay Khemka } 635c1921c63SVijay Khemka 636c1921c63SVijay Khemka /* If there is no sel then return erase complete */ 637c1921c63SVijay Khemka if (selObj.getCount() == 0) 638c1921c63SVijay Khemka { 639c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 640c1921c63SVijay Khemka } 641c1921c63SVijay Khemka 642c1921c63SVijay Khemka /* Erasure status cannot be fetched, so always return erasure 643c1921c63SVijay Khemka * status as `erase completed`. 644c1921c63SVijay Khemka */ 645c1921c63SVijay Khemka if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus) 646c1921c63SVijay Khemka { 647c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 648c1921c63SVijay Khemka } 649c1921c63SVijay Khemka 650c1921c63SVijay Khemka /* Check that initiate erase is correct */ 651c1921c63SVijay Khemka if (eraseOperation != fb_oem::ipmi::sel::initiateErase) 652c1921c63SVijay Khemka { 653c1921c63SVijay Khemka return ipmi::responseInvalidFieldRequest(); 654c1921c63SVijay Khemka } 655c1921c63SVijay Khemka 656c1921c63SVijay Khemka /* Per the IPMI spec, need to cancel any reservation when the 657c1921c63SVijay Khemka * SEL is cleared 658c1921c63SVijay Khemka */ 659c1921c63SVijay Khemka cancelSELReservation(); 660c1921c63SVijay Khemka 661c1921c63SVijay Khemka /* Clear the complete Sel Json object */ 662c1921c63SVijay Khemka if (selObj.clear() < 0) 663c1921c63SVijay Khemka { 664c1921c63SVijay Khemka return ipmi::responseUnspecifiedError(); 665c1921c63SVijay Khemka } 666c1921c63SVijay Khemka 667c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 668c1921c63SVijay Khemka } 669c1921c63SVijay Khemka 670c1921c63SVijay Khemka ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 671c1921c63SVijay Khemka { 672c1921c63SVijay Khemka struct timespec selTime = {}; 673c1921c63SVijay Khemka 674c1921c63SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 675c1921c63SVijay Khemka { 676c1921c63SVijay Khemka return ipmi::responseUnspecifiedError(); 677c1921c63SVijay Khemka } 678c1921c63SVijay Khemka 679c1921c63SVijay Khemka return ipmi::responseSuccess(selTime.tv_sec); 680c1921c63SVijay Khemka } 681c1921c63SVijay Khemka 682c1921c63SVijay Khemka ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime) 683c1921c63SVijay Khemka { 684c1921c63SVijay Khemka // Set SEL Time is not supported 685c1921c63SVijay Khemka return ipmi::responseInvalidCommand(); 686c1921c63SVijay Khemka } 687c1921c63SVijay Khemka 688c1921c63SVijay Khemka ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset() 689c1921c63SVijay Khemka { 690c1921c63SVijay Khemka /* TODO: For now, the SEL time stamp is based on UTC time, 691c1921c63SVijay Khemka * so return 0x0000 as offset. Might need to change once 692c1921c63SVijay Khemka * supporting zones in SEL time stamps 693c1921c63SVijay Khemka */ 694c1921c63SVijay Khemka 695c1921c63SVijay Khemka uint16_t utcOffset = 0x0000; 696c1921c63SVijay Khemka return ipmi::responseSuccess(utcOffset); 697c1921c63SVijay Khemka } 698c1921c63SVijay Khemka 69911b9c3b1SVijay Khemka void registerSELFunctions() 70011b9c3b1SVijay Khemka { 70111b9c3b1SVijay Khemka // <Get SEL Info> 70211b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 70311b9c3b1SVijay Khemka ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 70411b9c3b1SVijay Khemka ipmiStorageGetSELInfo); 70511b9c3b1SVijay Khemka 70611b9c3b1SVijay Khemka // <Get SEL Entry> 70711b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 70811b9c3b1SVijay Khemka ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 70911b9c3b1SVijay Khemka ipmiStorageGetSELEntry); 71011b9c3b1SVijay Khemka 71111b9c3b1SVijay Khemka // <Add SEL Entry> 71211b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 71311b9c3b1SVijay Khemka ipmi::storage::cmdAddSelEntry, 71411b9c3b1SVijay Khemka ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 71511b9c3b1SVijay Khemka 716c1921c63SVijay Khemka // <Clear SEL> 717c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 718c1921c63SVijay Khemka ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 719c1921c63SVijay Khemka ipmiStorageClearSEL); 720c1921c63SVijay Khemka 721c1921c63SVijay Khemka // <Get SEL Time> 722c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 723c1921c63SVijay Khemka ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 724c1921c63SVijay Khemka ipmiStorageGetSELTime); 725c1921c63SVijay Khemka 726c1921c63SVijay Khemka // <Set SEL Time> 727c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 728c1921c63SVijay Khemka ipmi::storage::cmdSetSelTime, 729c1921c63SVijay Khemka ipmi::Privilege::Operator, ipmiStorageSetSELTime); 730c1921c63SVijay Khemka 731c1921c63SVijay Khemka // <Get SEL Time UTC Offset> 732c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 733c1921c63SVijay Khemka ipmi::storage::cmdGetSelTimeUtcOffset, 734c1921c63SVijay Khemka ipmi::Privilege::User, 735c1921c63SVijay Khemka ipmiStorageGetSELTimeUtcOffset); 736c1921c63SVijay Khemka 73711b9c3b1SVijay Khemka return; 73811b9c3b1SVijay Khemka } 73911b9c3b1SVijay Khemka 74011b9c3b1SVijay Khemka } // namespace storage 74111b9c3b1SVijay Khemka } // namespace ipmi 742