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 184*f36f345fSVijay Khemka static void parseStdSel(StdSELEntry *data, std::string &errStr) 185*f36f345fSVijay Khemka { 186*f36f345fSVijay Khemka std::stringstream tmpStream; 187*f36f345fSVijay Khemka tmpStream << std::hex << std::uppercase; 188*f36f345fSVijay Khemka 189*f36f345fSVijay Khemka /* TODO: add pal_add_cri_sel */ 190*f36f345fSVijay Khemka switch (data->sensorNum) 191*f36f345fSVijay Khemka { 192*f36f345fSVijay Khemka case memoryEccError: 193*f36f345fSVijay Khemka switch (data->eventData1 & 0x0F) 194*f36f345fSVijay Khemka { 195*f36f345fSVijay Khemka case 0x00: 196*f36f345fSVijay Khemka errStr = "Correctable"; 197*f36f345fSVijay Khemka tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 198*f36f345fSVijay Khemka << data->eventData3 << " ECC err"; 199*f36f345fSVijay Khemka break; 200*f36f345fSVijay Khemka case 0x01: 201*f36f345fSVijay Khemka errStr = "Uncorrectable"; 202*f36f345fSVijay Khemka tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 203*f36f345fSVijay Khemka << data->eventData3 << " UECC err"; 204*f36f345fSVijay Khemka break; 205*f36f345fSVijay Khemka case 0x02: 206*f36f345fSVijay Khemka errStr = "Parity"; 207*f36f345fSVijay Khemka break; 208*f36f345fSVijay Khemka case 0x05: 209*f36f345fSVijay Khemka errStr = "Correctable ECC error Logging Limit Reached"; 210*f36f345fSVijay Khemka break; 211*f36f345fSVijay Khemka default: 212*f36f345fSVijay Khemka errStr = "Unknown"; 213*f36f345fSVijay Khemka } 214*f36f345fSVijay Khemka break; 215*f36f345fSVijay Khemka case memoryErrLogDIS: 216*f36f345fSVijay Khemka if ((data->eventData1 & 0x0F) == 0) 217*f36f345fSVijay Khemka { 218*f36f345fSVijay Khemka errStr = "Correctable Memory Error Logging Disabled"; 219*f36f345fSVijay Khemka } 220*f36f345fSVijay Khemka else 221*f36f345fSVijay Khemka { 222*f36f345fSVijay Khemka errStr = "Unknown"; 223*f36f345fSVijay Khemka } 224*f36f345fSVijay Khemka break; 225*f36f345fSVijay Khemka default: 226*f36f345fSVijay Khemka 227*f36f345fSVijay Khemka /* TODO: parse sel helper */ 228*f36f345fSVijay Khemka errStr = "Unknown"; 229*f36f345fSVijay Khemka return; 230*f36f345fSVijay Khemka } 231*f36f345fSVijay Khemka 232*f36f345fSVijay Khemka errStr += " (DIMM " + std::to_string(data->eventData3) + ")"; 233*f36f345fSVijay Khemka errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03); 234*f36f345fSVijay Khemka 235*f36f345fSVijay Khemka switch ((data->eventData2 & 0x0C) >> 2) 236*f36f345fSVijay Khemka { 237*f36f345fSVijay Khemka case 0x00: 238*f36f345fSVijay Khemka // Ignore when " All info available" 239*f36f345fSVijay Khemka break; 240*f36f345fSVijay Khemka case 0x01: 241*f36f345fSVijay Khemka errStr += " DIMM info not valid"; 242*f36f345fSVijay Khemka break; 243*f36f345fSVijay Khemka case 0x02: 244*f36f345fSVijay Khemka errStr += " CHN info not valid"; 245*f36f345fSVijay Khemka break; 246*f36f345fSVijay Khemka case 0x03: 247*f36f345fSVijay Khemka errStr += " CPU info not valid"; 248*f36f345fSVijay Khemka break; 249*f36f345fSVijay Khemka default: 250*f36f345fSVijay Khemka errStr += " Unknown"; 251*f36f345fSVijay Khemka } 252*f36f345fSVijay Khemka 253*f36f345fSVijay Khemka if (((data->eventType & 0x80) >> 7) == 0) 254*f36f345fSVijay Khemka { 255*f36f345fSVijay Khemka errStr += " Assertion"; 256*f36f345fSVijay Khemka } 257*f36f345fSVijay Khemka else 258*f36f345fSVijay Khemka { 259*f36f345fSVijay Khemka errStr += " Deassertion"; 260*f36f345fSVijay Khemka } 261*f36f345fSVijay Khemka 262*f36f345fSVijay Khemka return; 263*f36f345fSVijay Khemka } 264*f36f345fSVijay Khemka 265*f36f345fSVijay Khemka static void parseOemSel(TsOemSELEntry *data, std::string &errStr) 266*f36f345fSVijay Khemka { 267*f36f345fSVijay Khemka std::stringstream tmpStream; 268*f36f345fSVijay Khemka tmpStream << std::hex << std::uppercase << std::setfill('0'); 269*f36f345fSVijay Khemka 270*f36f345fSVijay Khemka switch (data->recordType) 271*f36f345fSVijay Khemka { 272*f36f345fSVijay Khemka case 0xC0: 273*f36f345fSVijay Khemka tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1] 274*f36f345fSVijay Khemka << std::setw(2) << (int)data->oemData[0] << " DID:0x" 275*f36f345fSVijay Khemka << std::setw(2) << (int)data->oemData[3] << std::setw(2) 276*f36f345fSVijay Khemka << (int)data->oemData[2] << " Slot:0x" << std::setw(2) 277*f36f345fSVijay Khemka << (int)data->oemData[4] << " Error ID:0x" << std::setw(2) 278*f36f345fSVijay Khemka << (int)data->oemData[5]; 279*f36f345fSVijay Khemka break; 280*f36f345fSVijay Khemka case 0xC2: 281*f36f345fSVijay Khemka tmpStream << "Extra info:0x" << std::setw(2) 282*f36f345fSVijay Khemka << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2) 283*f36f345fSVijay Khemka << (int)data->oemData[3] << std::setw(2) 284*f36f345fSVijay Khemka << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2) 285*f36f345fSVijay Khemka << (int)data->oemData[5] << std::setw(2) 286*f36f345fSVijay Khemka << (int)data->oemData[4]; 287*f36f345fSVijay Khemka break; 288*f36f345fSVijay Khemka case 0xC3: 289*f36f345fSVijay Khemka int bank = (data->oemData[1] & 0xf0) >> 4; 290*f36f345fSVijay Khemka int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2]; 291*f36f345fSVijay Khemka 292*f36f345fSVijay Khemka tmpStream << "Fail Device:0x" << std::setw(2) 293*f36f345fSVijay Khemka << (int)data->oemData[0] << " Bank:0x" << std::setw(2) 294*f36f345fSVijay Khemka << bank << " Column:0x" << std::setw(2) << col 295*f36f345fSVijay Khemka << " Failed Row:0x" << std::setw(2) 296*f36f345fSVijay Khemka << (int)data->oemData[3] << std::setw(2) 297*f36f345fSVijay Khemka << (int)data->oemData[4] << std::setw(2) 298*f36f345fSVijay Khemka << (int)data->oemData[5]; 299*f36f345fSVijay Khemka } 300*f36f345fSVijay Khemka 301*f36f345fSVijay Khemka errStr = tmpStream.str(); 302*f36f345fSVijay Khemka 303*f36f345fSVijay Khemka return; 304*f36f345fSVijay Khemka } 305*f36f345fSVijay Khemka 306*f36f345fSVijay Khemka static void parseSelData(std::vector<uint8_t> &reqData, std::string &msgLog) 307*f36f345fSVijay Khemka { 308*f36f345fSVijay Khemka 309*f36f345fSVijay Khemka /* Get record type */ 310*f36f345fSVijay Khemka int recType = reqData[2]; 311*f36f345fSVijay Khemka std::string errType, errLog; 312*f36f345fSVijay Khemka 313*f36f345fSVijay Khemka uint8_t *ptr = NULL; 314*f36f345fSVijay Khemka 315*f36f345fSVijay Khemka std::stringstream recTypeStream; 316*f36f345fSVijay Khemka recTypeStream << std::hex << std::uppercase << std::setfill('0') 317*f36f345fSVijay Khemka << std::setw(2) << recType; 318*f36f345fSVijay Khemka 319*f36f345fSVijay Khemka msgLog = "SEL Entry: FRU: 1, Record: "; 320*f36f345fSVijay Khemka 321*f36f345fSVijay Khemka if (recType == stdErrType) 322*f36f345fSVijay Khemka { 323*f36f345fSVijay Khemka StdSELEntry *data = reinterpret_cast<StdSELEntry *>(&reqData[0]); 324*f36f345fSVijay Khemka std::string sensorName; 325*f36f345fSVijay Khemka 326*f36f345fSVijay Khemka errType = stdErr; 327*f36f345fSVijay Khemka if (data->sensorType == 0x1F) 328*f36f345fSVijay Khemka { 329*f36f345fSVijay Khemka sensorName = "OS"; 330*f36f345fSVijay Khemka } 331*f36f345fSVijay Khemka else 332*f36f345fSVijay Khemka { 333*f36f345fSVijay Khemka auto findSensorName = sensorNameTable.find(data->sensorNum); 334*f36f345fSVijay Khemka if (findSensorName == sensorNameTable.end()) 335*f36f345fSVijay Khemka { 336*f36f345fSVijay Khemka sensorName = "Unknown"; 337*f36f345fSVijay Khemka } 338*f36f345fSVijay Khemka else 339*f36f345fSVijay Khemka { 340*f36f345fSVijay Khemka sensorName = findSensorName->second; 341*f36f345fSVijay Khemka } 342*f36f345fSVijay Khemka } 343*f36f345fSVijay Khemka 344*f36f345fSVijay Khemka std::tm *ts = localtime((time_t *)(&(data->timeStamp))); 345*f36f345fSVijay Khemka std::string timeStr = std::asctime(ts); 346*f36f345fSVijay Khemka 347*f36f345fSVijay Khemka parseStdSel(data, errLog); 348*f36f345fSVijay Khemka ptr = &(data->eventData1); 349*f36f345fSVijay Khemka std::vector<uint8_t> evtData(ptr, ptr + 3); 350*f36f345fSVijay Khemka std::string eventData; 351*f36f345fSVijay Khemka toHexStr(evtData, eventData); 352*f36f345fSVijay Khemka 353*f36f345fSVijay Khemka std::stringstream senNumStream; 354*f36f345fSVijay Khemka senNumStream << std::hex << std::uppercase << std::setfill('0') 355*f36f345fSVijay Khemka << std::setw(2) << (int)(data->sensorNum); 356*f36f345fSVijay Khemka 357*f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + 358*f36f345fSVijay Khemka "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" + 359*f36f345fSVijay Khemka senNumStream.str() + "), Event Data: (" + eventData + ") " + 360*f36f345fSVijay Khemka errLog; 361*f36f345fSVijay Khemka } 362*f36f345fSVijay Khemka else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax)) 363*f36f345fSVijay Khemka { 364*f36f345fSVijay Khemka /* timestamped OEM SEL records */ 365*f36f345fSVijay Khemka TsOemSELEntry *data = reinterpret_cast<TsOemSELEntry *>(&reqData[0]); 366*f36f345fSVijay Khemka ptr = data->mfrId; 367*f36f345fSVijay Khemka std::vector<uint8_t> mfrIdData(ptr, ptr + 3); 368*f36f345fSVijay Khemka std::string mfrIdStr; 369*f36f345fSVijay Khemka toHexStr(mfrIdData, mfrIdStr); 370*f36f345fSVijay Khemka 371*f36f345fSVijay Khemka ptr = data->oemData; 372*f36f345fSVijay Khemka std::vector<uint8_t> oemData(ptr, ptr + 6); 373*f36f345fSVijay Khemka std::string oemDataStr; 374*f36f345fSVijay Khemka toHexStr(oemData, oemDataStr); 375*f36f345fSVijay Khemka 376*f36f345fSVijay Khemka std::tm *ts = localtime((time_t *)(&(data->timeStamp))); 377*f36f345fSVijay Khemka std::string timeStr = std::asctime(ts); 378*f36f345fSVijay Khemka 379*f36f345fSVijay Khemka errType = oemTSErr; 380*f36f345fSVijay Khemka parseOemSel(data, errLog); 381*f36f345fSVijay Khemka 382*f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + 383*f36f345fSVijay Khemka "), Time: " + timeStr + ", MFG ID: " + mfrIdStr + 384*f36f345fSVijay Khemka ", OEM Data: (" + oemDataStr + ") " + errLog; 385*f36f345fSVijay Khemka } 386*f36f345fSVijay Khemka else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax)) 387*f36f345fSVijay Khemka { 388*f36f345fSVijay Khemka /* Non timestamped OEM SEL records */ 389*f36f345fSVijay Khemka NtsOemSELEntry *data = reinterpret_cast<NtsOemSELEntry *>(&reqData[0]); 390*f36f345fSVijay Khemka errType = oemNTSErr; 391*f36f345fSVijay Khemka 392*f36f345fSVijay Khemka ptr = data->oemData; 393*f36f345fSVijay Khemka std::vector<uint8_t> oemData(ptr, ptr + 13); 394*f36f345fSVijay Khemka std::string oemDataStr; 395*f36f345fSVijay Khemka toHexStr(oemData, oemDataStr); 396*f36f345fSVijay Khemka 397*f36f345fSVijay Khemka parseOemSel((TsOemSELEntry *)data, errLog); 398*f36f345fSVijay Khemka msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" + 399*f36f345fSVijay Khemka oemDataStr + ") " + errLog; 400*f36f345fSVijay Khemka } 401*f36f345fSVijay Khemka else 402*f36f345fSVijay Khemka { 403*f36f345fSVijay Khemka errType = unknownErr; 404*f36f345fSVijay Khemka toHexStr(reqData, errLog); 405*f36f345fSVijay Khemka msgLog += 406*f36f345fSVijay Khemka errType + " (0x" + recTypeStream.str() + ") RawData: " + errLog; 407*f36f345fSVijay Khemka } 408*f36f345fSVijay Khemka } 409*f36f345fSVijay Khemka 41011b9c3b1SVijay Khemka } // namespace fb_oem::ipmi::sel 41111b9c3b1SVijay Khemka 41211b9c3b1SVijay Khemka namespace ipmi 41311b9c3b1SVijay Khemka { 41411b9c3b1SVijay Khemka 41511b9c3b1SVijay Khemka namespace storage 41611b9c3b1SVijay Khemka { 41711b9c3b1SVijay Khemka 41811b9c3b1SVijay Khemka static void registerSELFunctions() __attribute__((constructor)); 41911b9c3b1SVijay Khemka static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101))); 42011b9c3b1SVijay Khemka 42111b9c3b1SVijay Khemka ipmi::RspType<uint8_t, // SEL version 42211b9c3b1SVijay Khemka uint16_t, // SEL entry count 42311b9c3b1SVijay Khemka uint16_t, // free space 42411b9c3b1SVijay Khemka uint32_t, // last add timestamp 42511b9c3b1SVijay Khemka uint32_t, // last erase timestamp 42611b9c3b1SVijay Khemka uint8_t> // operation support 42711b9c3b1SVijay Khemka ipmiStorageGetSELInfo() 42811b9c3b1SVijay Khemka { 42911b9c3b1SVijay Khemka 43011b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELInfoData info; 43111b9c3b1SVijay Khemka 43211b9c3b1SVijay Khemka selObj.getInfo(info); 43311b9c3b1SVijay Khemka return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace, 43411b9c3b1SVijay Khemka info.addTimeStamp, info.eraseTimeStamp, 43511b9c3b1SVijay Khemka info.operationSupport); 43611b9c3b1SVijay Khemka } 43711b9c3b1SVijay Khemka 43811b9c3b1SVijay Khemka ipmi::RspType<uint16_t, std::vector<uint8_t>> 43911b9c3b1SVijay Khemka ipmiStorageGetSELEntry(std::vector<uint8_t> data) 44011b9c3b1SVijay Khemka { 44111b9c3b1SVijay Khemka 44211b9c3b1SVijay Khemka if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest)) 44311b9c3b1SVijay Khemka { 44411b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 44511b9c3b1SVijay Khemka } 44611b9c3b1SVijay Khemka 44711b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELEntryRequest *reqData = 44811b9c3b1SVijay Khemka reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest *>(&data[0]); 44911b9c3b1SVijay Khemka 45011b9c3b1SVijay Khemka if (reqData->reservID != 0) 45111b9c3b1SVijay Khemka { 45211b9c3b1SVijay Khemka if (!checkSELReservation(reqData->reservID)) 45311b9c3b1SVijay Khemka { 45411b9c3b1SVijay Khemka return ipmi::responseInvalidReservationId(); 45511b9c3b1SVijay Khemka } 45611b9c3b1SVijay Khemka } 45711b9c3b1SVijay Khemka 45811b9c3b1SVijay Khemka uint16_t selCnt = selObj.getCount(); 45911b9c3b1SVijay Khemka if (selCnt == 0) 46011b9c3b1SVijay Khemka { 46111b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 46211b9c3b1SVijay Khemka } 46311b9c3b1SVijay Khemka 46411b9c3b1SVijay Khemka /* If it is asked for first entry */ 46511b9c3b1SVijay Khemka if (reqData->recordID == fb_oem::ipmi::sel::firstEntry) 46611b9c3b1SVijay Khemka { 46711b9c3b1SVijay Khemka /* First Entry (0x0000) as per Spec */ 46811b9c3b1SVijay Khemka reqData->recordID = 1; 46911b9c3b1SVijay Khemka } 47011b9c3b1SVijay Khemka else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry) 47111b9c3b1SVijay Khemka { 47211b9c3b1SVijay Khemka /* Last entry (0xFFFF) as per Spec */ 47311b9c3b1SVijay Khemka reqData->recordID = selCnt; 47411b9c3b1SVijay Khemka } 47511b9c3b1SVijay Khemka 47611b9c3b1SVijay Khemka std::string ipmiRaw; 47711b9c3b1SVijay Khemka 47811b9c3b1SVijay Khemka if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0) 47911b9c3b1SVijay Khemka { 48011b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 48111b9c3b1SVijay Khemka } 48211b9c3b1SVijay Khemka 48311b9c3b1SVijay Khemka std::vector<uint8_t> recDataBytes; 48411b9c3b1SVijay Khemka if (fromHexStr(ipmiRaw, recDataBytes) < 0) 48511b9c3b1SVijay Khemka { 48611b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 48711b9c3b1SVijay Khemka } 48811b9c3b1SVijay Khemka 48911b9c3b1SVijay Khemka /* Identify the next SEL record ID. If recordID is same as 49011b9c3b1SVijay Khemka * total SeL count then next id should be last entry else 49111b9c3b1SVijay Khemka * it should be incremented by 1 to current RecordID 49211b9c3b1SVijay Khemka */ 49311b9c3b1SVijay Khemka uint16_t nextRecord; 49411b9c3b1SVijay Khemka if (reqData->recordID == selCnt) 49511b9c3b1SVijay Khemka { 49611b9c3b1SVijay Khemka nextRecord = fb_oem::ipmi::sel::lastEntry; 49711b9c3b1SVijay Khemka } 49811b9c3b1SVijay Khemka else 49911b9c3b1SVijay Khemka { 50011b9c3b1SVijay Khemka nextRecord = reqData->recordID + 1; 50111b9c3b1SVijay Khemka } 50211b9c3b1SVijay Khemka 50311b9c3b1SVijay Khemka if (reqData->readLen == fb_oem::ipmi::sel::entireRecord) 50411b9c3b1SVijay Khemka { 50511b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recDataBytes); 50611b9c3b1SVijay Khemka } 50711b9c3b1SVijay Khemka else 50811b9c3b1SVijay Khemka { 50911b9c3b1SVijay Khemka if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize || 51011b9c3b1SVijay Khemka reqData->readLen > fb_oem::ipmi::sel::selRecordSize) 51111b9c3b1SVijay Khemka { 51211b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 51311b9c3b1SVijay Khemka } 51411b9c3b1SVijay Khemka std::vector<uint8_t> recPartData; 51511b9c3b1SVijay Khemka 51611b9c3b1SVijay Khemka auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset; 51711b9c3b1SVijay Khemka auto readLength = std::min(diff, static_cast<int>(reqData->readLen)); 51811b9c3b1SVijay Khemka 51911b9c3b1SVijay Khemka for (int i = 0; i < readLength; i++) 52011b9c3b1SVijay Khemka { 52111b9c3b1SVijay Khemka recPartData.push_back(recDataBytes[i + reqData->offset]); 52211b9c3b1SVijay Khemka } 52311b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recPartData); 52411b9c3b1SVijay Khemka } 52511b9c3b1SVijay Khemka } 52611b9c3b1SVijay Khemka 52711b9c3b1SVijay Khemka ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(std::vector<uint8_t> data) 52811b9c3b1SVijay Khemka { 52911b9c3b1SVijay Khemka /* Per the IPMI spec, need to cancel any reservation when a 53011b9c3b1SVijay Khemka * SEL entry is added 53111b9c3b1SVijay Khemka */ 53211b9c3b1SVijay Khemka cancelSELReservation(); 53311b9c3b1SVijay Khemka 53411b9c3b1SVijay Khemka if (data.size() != fb_oem::ipmi::sel::selRecordSize) 53511b9c3b1SVijay Khemka { 53611b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 53711b9c3b1SVijay Khemka } 53811b9c3b1SVijay Khemka 53911b9c3b1SVijay Khemka std::string ipmiRaw, logErr; 54011b9c3b1SVijay Khemka toHexStr(data, ipmiRaw); 54111b9c3b1SVijay Khemka 542*f36f345fSVijay Khemka /* Parse sel data and get an error log to be filed */ 543*f36f345fSVijay Khemka fb_oem::ipmi::sel::parseSelData(data, logErr); 544*f36f345fSVijay Khemka 54511b9c3b1SVijay Khemka /* Log the Raw SEL message to the journal */ 54611b9c3b1SVijay Khemka std::string journalMsg = "SEL Entry Added: " + ipmiRaw; 547*f36f345fSVijay Khemka 54811b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(journalMsg.c_str()); 549*f36f345fSVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(logErr.c_str()); 55011b9c3b1SVijay Khemka 55111b9c3b1SVijay Khemka int responseID = selObj.addEntry(ipmiRaw.c_str()); 55211b9c3b1SVijay Khemka if (responseID < 0) 55311b9c3b1SVijay Khemka { 55411b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 55511b9c3b1SVijay Khemka } 55611b9c3b1SVijay Khemka return ipmi::responseSuccess((uint16_t)responseID); 55711b9c3b1SVijay Khemka } 55811b9c3b1SVijay Khemka 559c1921c63SVijay Khemka ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID, 560c1921c63SVijay Khemka const std::array<uint8_t, 3> &clr, 561c1921c63SVijay Khemka uint8_t eraseOperation) 562c1921c63SVijay Khemka { 563c1921c63SVijay Khemka if (!checkSELReservation(reservationID)) 564c1921c63SVijay Khemka { 565c1921c63SVijay Khemka return ipmi::responseInvalidReservationId(); 566c1921c63SVijay Khemka } 567c1921c63SVijay Khemka 568c1921c63SVijay Khemka static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 569c1921c63SVijay Khemka if (clr != clrExpected) 570c1921c63SVijay Khemka { 571c1921c63SVijay Khemka return ipmi::responseInvalidFieldRequest(); 572c1921c63SVijay Khemka } 573c1921c63SVijay Khemka 574c1921c63SVijay Khemka /* If there is no sel then return erase complete */ 575c1921c63SVijay Khemka if (selObj.getCount() == 0) 576c1921c63SVijay Khemka { 577c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 578c1921c63SVijay Khemka } 579c1921c63SVijay Khemka 580c1921c63SVijay Khemka /* Erasure status cannot be fetched, so always return erasure 581c1921c63SVijay Khemka * status as `erase completed`. 582c1921c63SVijay Khemka */ 583c1921c63SVijay Khemka if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus) 584c1921c63SVijay Khemka { 585c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 586c1921c63SVijay Khemka } 587c1921c63SVijay Khemka 588c1921c63SVijay Khemka /* Check that initiate erase is correct */ 589c1921c63SVijay Khemka if (eraseOperation != fb_oem::ipmi::sel::initiateErase) 590c1921c63SVijay Khemka { 591c1921c63SVijay Khemka return ipmi::responseInvalidFieldRequest(); 592c1921c63SVijay Khemka } 593c1921c63SVijay Khemka 594c1921c63SVijay Khemka /* Per the IPMI spec, need to cancel any reservation when the 595c1921c63SVijay Khemka * SEL is cleared 596c1921c63SVijay Khemka */ 597c1921c63SVijay Khemka cancelSELReservation(); 598c1921c63SVijay Khemka 599c1921c63SVijay Khemka /* Clear the complete Sel Json object */ 600c1921c63SVijay Khemka if (selObj.clear() < 0) 601c1921c63SVijay Khemka { 602c1921c63SVijay Khemka return ipmi::responseUnspecifiedError(); 603c1921c63SVijay Khemka } 604c1921c63SVijay Khemka 605c1921c63SVijay Khemka return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 606c1921c63SVijay Khemka } 607c1921c63SVijay Khemka 608c1921c63SVijay Khemka ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 609c1921c63SVijay Khemka { 610c1921c63SVijay Khemka struct timespec selTime = {}; 611c1921c63SVijay Khemka 612c1921c63SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 613c1921c63SVijay Khemka { 614c1921c63SVijay Khemka return ipmi::responseUnspecifiedError(); 615c1921c63SVijay Khemka } 616c1921c63SVijay Khemka 617c1921c63SVijay Khemka return ipmi::responseSuccess(selTime.tv_sec); 618c1921c63SVijay Khemka } 619c1921c63SVijay Khemka 620c1921c63SVijay Khemka ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime) 621c1921c63SVijay Khemka { 622c1921c63SVijay Khemka // Set SEL Time is not supported 623c1921c63SVijay Khemka return ipmi::responseInvalidCommand(); 624c1921c63SVijay Khemka } 625c1921c63SVijay Khemka 626c1921c63SVijay Khemka ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset() 627c1921c63SVijay Khemka { 628c1921c63SVijay Khemka /* TODO: For now, the SEL time stamp is based on UTC time, 629c1921c63SVijay Khemka * so return 0x0000 as offset. Might need to change once 630c1921c63SVijay Khemka * supporting zones in SEL time stamps 631c1921c63SVijay Khemka */ 632c1921c63SVijay Khemka 633c1921c63SVijay Khemka uint16_t utcOffset = 0x0000; 634c1921c63SVijay Khemka return ipmi::responseSuccess(utcOffset); 635c1921c63SVijay Khemka } 636c1921c63SVijay Khemka 63711b9c3b1SVijay Khemka void registerSELFunctions() 63811b9c3b1SVijay Khemka { 63911b9c3b1SVijay Khemka // <Get SEL Info> 64011b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 64111b9c3b1SVijay Khemka ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 64211b9c3b1SVijay Khemka ipmiStorageGetSELInfo); 64311b9c3b1SVijay Khemka 64411b9c3b1SVijay Khemka // <Get SEL Entry> 64511b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 64611b9c3b1SVijay Khemka ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 64711b9c3b1SVijay Khemka ipmiStorageGetSELEntry); 64811b9c3b1SVijay Khemka 64911b9c3b1SVijay Khemka // <Add SEL Entry> 65011b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 65111b9c3b1SVijay Khemka ipmi::storage::cmdAddSelEntry, 65211b9c3b1SVijay Khemka ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 65311b9c3b1SVijay Khemka 654c1921c63SVijay Khemka // <Clear SEL> 655c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 656c1921c63SVijay Khemka ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 657c1921c63SVijay Khemka ipmiStorageClearSEL); 658c1921c63SVijay Khemka 659c1921c63SVijay Khemka // <Get SEL Time> 660c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 661c1921c63SVijay Khemka ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 662c1921c63SVijay Khemka ipmiStorageGetSELTime); 663c1921c63SVijay Khemka 664c1921c63SVijay Khemka // <Set SEL Time> 665c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 666c1921c63SVijay Khemka ipmi::storage::cmdSetSelTime, 667c1921c63SVijay Khemka ipmi::Privilege::Operator, ipmiStorageSetSELTime); 668c1921c63SVijay Khemka 669c1921c63SVijay Khemka // <Get SEL Time UTC Offset> 670c1921c63SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 671c1921c63SVijay Khemka ipmi::storage::cmdGetSelTimeUtcOffset, 672c1921c63SVijay Khemka ipmi::Privilege::User, 673c1921c63SVijay Khemka ipmiStorageGetSELTimeUtcOffset); 674c1921c63SVijay Khemka 67511b9c3b1SVijay Khemka return; 67611b9c3b1SVijay Khemka } 67711b9c3b1SVijay Khemka 67811b9c3b1SVijay Khemka } // namespace storage 67911b9c3b1SVijay Khemka } // namespace ipmi 680