1*11b9c3b1SVijay Khemka /* 2*11b9c3b1SVijay Khemka * Copyright (c) 2018 Intel Corporation. 3*11b9c3b1SVijay Khemka * Copyright (c) 2018-present Facebook. 4*11b9c3b1SVijay Khemka * 5*11b9c3b1SVijay Khemka * Licensed under the Apache License, Version 2.0 (the "License"); 6*11b9c3b1SVijay Khemka * you may not use this file except in compliance with the License. 7*11b9c3b1SVijay Khemka * You may obtain a copy of the License at 8*11b9c3b1SVijay Khemka * 9*11b9c3b1SVijay Khemka * http://www.apache.org/licenses/LICENSE-2.0 10*11b9c3b1SVijay Khemka * 11*11b9c3b1SVijay Khemka * Unless required by applicable law or agreed to in writing, software 12*11b9c3b1SVijay Khemka * distributed under the License is distributed on an "AS IS" BASIS, 13*11b9c3b1SVijay Khemka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*11b9c3b1SVijay Khemka * See the License for the specific language governing permissions and 15*11b9c3b1SVijay Khemka * limitations under the License. 16*11b9c3b1SVijay Khemka */ 17*11b9c3b1SVijay Khemka 18*11b9c3b1SVijay Khemka #include <ipmid/api.hpp> 19*11b9c3b1SVijay Khemka 20*11b9c3b1SVijay Khemka #include <boost/algorithm/string/join.hpp> 21*11b9c3b1SVijay Khemka #include <nlohmann/json.hpp> 22*11b9c3b1SVijay Khemka #include <iostream> 23*11b9c3b1SVijay Khemka #include <sstream> 24*11b9c3b1SVijay Khemka #include <fstream> 25*11b9c3b1SVijay Khemka #include <phosphor-logging/log.hpp> 26*11b9c3b1SVijay Khemka #include <sdbusplus/message/types.hpp> 27*11b9c3b1SVijay Khemka #include <sdbusplus/timer.hpp> 28*11b9c3b1SVijay Khemka #include <storagecommands.hpp> 29*11b9c3b1SVijay Khemka 30*11b9c3b1SVijay Khemka //---------------------------------------------------------------------- 31*11b9c3b1SVijay Khemka // Platform specific functions for storing app data 32*11b9c3b1SVijay Khemka //---------------------------------------------------------------------- 33*11b9c3b1SVijay Khemka 34*11b9c3b1SVijay Khemka static void toHexStr(std::vector<uint8_t> &bytes, std::string &hexStr) 35*11b9c3b1SVijay Khemka { 36*11b9c3b1SVijay Khemka std::stringstream stream; 37*11b9c3b1SVijay Khemka stream << std::hex << std::uppercase << std::setfill('0'); 38*11b9c3b1SVijay Khemka for (const uint8_t byte : bytes) 39*11b9c3b1SVijay Khemka { 40*11b9c3b1SVijay Khemka stream << std::setw(2) << static_cast<int>(byte); 41*11b9c3b1SVijay Khemka } 42*11b9c3b1SVijay Khemka hexStr = stream.str(); 43*11b9c3b1SVijay Khemka } 44*11b9c3b1SVijay Khemka 45*11b9c3b1SVijay Khemka static int fromHexStr(const std::string hexStr, std::vector<uint8_t> &data) 46*11b9c3b1SVijay Khemka { 47*11b9c3b1SVijay Khemka for (unsigned int i = 0; i < hexStr.size(); i += 2) 48*11b9c3b1SVijay Khemka { 49*11b9c3b1SVijay Khemka try 50*11b9c3b1SVijay Khemka { 51*11b9c3b1SVijay Khemka data.push_back(static_cast<uint8_t>( 52*11b9c3b1SVijay Khemka std::stoul(hexStr.substr(i, 2), nullptr, 16))); 53*11b9c3b1SVijay Khemka } 54*11b9c3b1SVijay Khemka catch (std::invalid_argument &e) 55*11b9c3b1SVijay Khemka { 56*11b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 57*11b9c3b1SVijay Khemka return -1; 58*11b9c3b1SVijay Khemka } 59*11b9c3b1SVijay Khemka catch (std::out_of_range &e) 60*11b9c3b1SVijay Khemka { 61*11b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 62*11b9c3b1SVijay Khemka return -1; 63*11b9c3b1SVijay Khemka } 64*11b9c3b1SVijay Khemka } 65*11b9c3b1SVijay Khemka return 0; 66*11b9c3b1SVijay Khemka } 67*11b9c3b1SVijay Khemka 68*11b9c3b1SVijay Khemka namespace fb_oem::ipmi::sel 69*11b9c3b1SVijay Khemka { 70*11b9c3b1SVijay Khemka 71*11b9c3b1SVijay Khemka class SELData 72*11b9c3b1SVijay Khemka { 73*11b9c3b1SVijay Khemka private: 74*11b9c3b1SVijay Khemka nlohmann::json selDataObj; 75*11b9c3b1SVijay Khemka 76*11b9c3b1SVijay Khemka void flush() 77*11b9c3b1SVijay Khemka { 78*11b9c3b1SVijay Khemka std::ofstream file(SEL_JSON_DATA_FILE); 79*11b9c3b1SVijay Khemka file << selDataObj; 80*11b9c3b1SVijay Khemka file.close(); 81*11b9c3b1SVijay Khemka } 82*11b9c3b1SVijay Khemka 83*11b9c3b1SVijay Khemka void init() 84*11b9c3b1SVijay Khemka { 85*11b9c3b1SVijay Khemka selDataObj[KEY_SEL_VER] = 0x51; 86*11b9c3b1SVijay Khemka selDataObj[KEY_SEL_COUNT] = 0; 87*11b9c3b1SVijay Khemka selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF; 88*11b9c3b1SVijay Khemka selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF; 89*11b9c3b1SVijay Khemka selDataObj[KEY_OPER_SUPP] = 0x02; 90*11b9c3b1SVijay Khemka /* Spec indicates that more than 64kB is free */ 91*11b9c3b1SVijay Khemka selDataObj[KEY_FREE_SPACE] = 0xFFFF; 92*11b9c3b1SVijay Khemka } 93*11b9c3b1SVijay Khemka 94*11b9c3b1SVijay Khemka public: 95*11b9c3b1SVijay Khemka SELData() 96*11b9c3b1SVijay Khemka { 97*11b9c3b1SVijay Khemka /* Get App data stored in json file */ 98*11b9c3b1SVijay Khemka std::ifstream file(SEL_JSON_DATA_FILE); 99*11b9c3b1SVijay Khemka if (file) 100*11b9c3b1SVijay Khemka { 101*11b9c3b1SVijay Khemka file >> selDataObj; 102*11b9c3b1SVijay Khemka file.close(); 103*11b9c3b1SVijay Khemka } 104*11b9c3b1SVijay Khemka 105*11b9c3b1SVijay Khemka /* Initialize SelData object if no entries. */ 106*11b9c3b1SVijay Khemka if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end()) 107*11b9c3b1SVijay Khemka { 108*11b9c3b1SVijay Khemka init(); 109*11b9c3b1SVijay Khemka } 110*11b9c3b1SVijay Khemka } 111*11b9c3b1SVijay Khemka 112*11b9c3b1SVijay Khemka int clear() 113*11b9c3b1SVijay Khemka { 114*11b9c3b1SVijay Khemka /* Clear the complete Sel Json object */ 115*11b9c3b1SVijay Khemka selDataObj.clear(); 116*11b9c3b1SVijay Khemka /* Reinitialize it with basic data */ 117*11b9c3b1SVijay Khemka init(); 118*11b9c3b1SVijay Khemka /* Save the erase time */ 119*11b9c3b1SVijay Khemka struct timespec selTime = {}; 120*11b9c3b1SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 121*11b9c3b1SVijay Khemka { 122*11b9c3b1SVijay Khemka return -1; 123*11b9c3b1SVijay Khemka } 124*11b9c3b1SVijay Khemka selDataObj[KEY_ERASE_TIME] = selTime.tv_sec; 125*11b9c3b1SVijay Khemka flush(); 126*11b9c3b1SVijay Khemka return 0; 127*11b9c3b1SVijay Khemka } 128*11b9c3b1SVijay Khemka 129*11b9c3b1SVijay Khemka uint32_t getCount() 130*11b9c3b1SVijay Khemka { 131*11b9c3b1SVijay Khemka return selDataObj[KEY_SEL_COUNT]; 132*11b9c3b1SVijay Khemka } 133*11b9c3b1SVijay Khemka 134*11b9c3b1SVijay Khemka void getInfo(GetSELInfoData &info) 135*11b9c3b1SVijay Khemka { 136*11b9c3b1SVijay Khemka info.selVersion = selDataObj[KEY_SEL_VER]; 137*11b9c3b1SVijay Khemka info.entries = selDataObj[KEY_SEL_COUNT]; 138*11b9c3b1SVijay Khemka info.freeSpace = selDataObj[KEY_FREE_SPACE]; 139*11b9c3b1SVijay Khemka info.addTimeStamp = selDataObj[KEY_ADD_TIME]; 140*11b9c3b1SVijay Khemka info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME]; 141*11b9c3b1SVijay Khemka info.operationSupport = selDataObj[KEY_OPER_SUPP]; 142*11b9c3b1SVijay Khemka } 143*11b9c3b1SVijay Khemka 144*11b9c3b1SVijay Khemka int getEntry(uint32_t index, std::string &rawStr) 145*11b9c3b1SVijay Khemka { 146*11b9c3b1SVijay Khemka std::stringstream ss; 147*11b9c3b1SVijay Khemka ss << std::hex; 148*11b9c3b1SVijay Khemka ss << std::setw(2) << std::setfill('0') << index; 149*11b9c3b1SVijay Khemka 150*11b9c3b1SVijay Khemka /* Check or the requested SEL Entry, if record is available */ 151*11b9c3b1SVijay Khemka if (selDataObj.find(ss.str()) == selDataObj.end()) 152*11b9c3b1SVijay Khemka { 153*11b9c3b1SVijay Khemka return -1; 154*11b9c3b1SVijay Khemka } 155*11b9c3b1SVijay Khemka 156*11b9c3b1SVijay Khemka rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW]; 157*11b9c3b1SVijay Khemka return 0; 158*11b9c3b1SVijay Khemka } 159*11b9c3b1SVijay Khemka 160*11b9c3b1SVijay Khemka int addEntry(std::string keyStr) 161*11b9c3b1SVijay Khemka { 162*11b9c3b1SVijay Khemka struct timespec selTime = {}; 163*11b9c3b1SVijay Khemka 164*11b9c3b1SVijay Khemka if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 165*11b9c3b1SVijay Khemka { 166*11b9c3b1SVijay Khemka return -1; 167*11b9c3b1SVijay Khemka } 168*11b9c3b1SVijay Khemka 169*11b9c3b1SVijay Khemka selDataObj[KEY_ADD_TIME] = selTime.tv_sec; 170*11b9c3b1SVijay Khemka 171*11b9c3b1SVijay Khemka int selCount = selDataObj[KEY_SEL_COUNT]; 172*11b9c3b1SVijay Khemka selDataObj[KEY_SEL_COUNT] = ++selCount; 173*11b9c3b1SVijay Khemka 174*11b9c3b1SVijay Khemka std::stringstream ss; 175*11b9c3b1SVijay Khemka ss << std::hex; 176*11b9c3b1SVijay Khemka ss << std::setw(2) << std::setfill('0') << selCount; 177*11b9c3b1SVijay Khemka 178*11b9c3b1SVijay Khemka selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr; 179*11b9c3b1SVijay Khemka flush(); 180*11b9c3b1SVijay Khemka return selCount; 181*11b9c3b1SVijay Khemka } 182*11b9c3b1SVijay Khemka }; 183*11b9c3b1SVijay Khemka 184*11b9c3b1SVijay Khemka } // namespace fb_oem::ipmi::sel 185*11b9c3b1SVijay Khemka 186*11b9c3b1SVijay Khemka namespace ipmi 187*11b9c3b1SVijay Khemka { 188*11b9c3b1SVijay Khemka 189*11b9c3b1SVijay Khemka namespace storage 190*11b9c3b1SVijay Khemka { 191*11b9c3b1SVijay Khemka 192*11b9c3b1SVijay Khemka static void registerSELFunctions() __attribute__((constructor)); 193*11b9c3b1SVijay Khemka static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101))); 194*11b9c3b1SVijay Khemka 195*11b9c3b1SVijay Khemka ipmi::RspType<uint8_t, // SEL version 196*11b9c3b1SVijay Khemka uint16_t, // SEL entry count 197*11b9c3b1SVijay Khemka uint16_t, // free space 198*11b9c3b1SVijay Khemka uint32_t, // last add timestamp 199*11b9c3b1SVijay Khemka uint32_t, // last erase timestamp 200*11b9c3b1SVijay Khemka uint8_t> // operation support 201*11b9c3b1SVijay Khemka ipmiStorageGetSELInfo() 202*11b9c3b1SVijay Khemka { 203*11b9c3b1SVijay Khemka 204*11b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELInfoData info; 205*11b9c3b1SVijay Khemka 206*11b9c3b1SVijay Khemka selObj.getInfo(info); 207*11b9c3b1SVijay Khemka return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace, 208*11b9c3b1SVijay Khemka info.addTimeStamp, info.eraseTimeStamp, 209*11b9c3b1SVijay Khemka info.operationSupport); 210*11b9c3b1SVijay Khemka } 211*11b9c3b1SVijay Khemka 212*11b9c3b1SVijay Khemka ipmi::RspType<uint16_t, std::vector<uint8_t>> 213*11b9c3b1SVijay Khemka ipmiStorageGetSELEntry(std::vector<uint8_t> data) 214*11b9c3b1SVijay Khemka { 215*11b9c3b1SVijay Khemka 216*11b9c3b1SVijay Khemka if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest)) 217*11b9c3b1SVijay Khemka { 218*11b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 219*11b9c3b1SVijay Khemka } 220*11b9c3b1SVijay Khemka 221*11b9c3b1SVijay Khemka fb_oem::ipmi::sel::GetSELEntryRequest *reqData = 222*11b9c3b1SVijay Khemka reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest *>(&data[0]); 223*11b9c3b1SVijay Khemka 224*11b9c3b1SVijay Khemka if (reqData->reservID != 0) 225*11b9c3b1SVijay Khemka { 226*11b9c3b1SVijay Khemka if (!checkSELReservation(reqData->reservID)) 227*11b9c3b1SVijay Khemka { 228*11b9c3b1SVijay Khemka return ipmi::responseInvalidReservationId(); 229*11b9c3b1SVijay Khemka } 230*11b9c3b1SVijay Khemka } 231*11b9c3b1SVijay Khemka 232*11b9c3b1SVijay Khemka uint16_t selCnt = selObj.getCount(); 233*11b9c3b1SVijay Khemka if (selCnt == 0) 234*11b9c3b1SVijay Khemka { 235*11b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 236*11b9c3b1SVijay Khemka } 237*11b9c3b1SVijay Khemka 238*11b9c3b1SVijay Khemka /* If it is asked for first entry */ 239*11b9c3b1SVijay Khemka if (reqData->recordID == fb_oem::ipmi::sel::firstEntry) 240*11b9c3b1SVijay Khemka { 241*11b9c3b1SVijay Khemka /* First Entry (0x0000) as per Spec */ 242*11b9c3b1SVijay Khemka reqData->recordID = 1; 243*11b9c3b1SVijay Khemka } 244*11b9c3b1SVijay Khemka else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry) 245*11b9c3b1SVijay Khemka { 246*11b9c3b1SVijay Khemka /* Last entry (0xFFFF) as per Spec */ 247*11b9c3b1SVijay Khemka reqData->recordID = selCnt; 248*11b9c3b1SVijay Khemka } 249*11b9c3b1SVijay Khemka 250*11b9c3b1SVijay Khemka std::string ipmiRaw; 251*11b9c3b1SVijay Khemka 252*11b9c3b1SVijay Khemka if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0) 253*11b9c3b1SVijay Khemka { 254*11b9c3b1SVijay Khemka return ipmi::responseSensorInvalid(); 255*11b9c3b1SVijay Khemka } 256*11b9c3b1SVijay Khemka 257*11b9c3b1SVijay Khemka std::vector<uint8_t> recDataBytes; 258*11b9c3b1SVijay Khemka if (fromHexStr(ipmiRaw, recDataBytes) < 0) 259*11b9c3b1SVijay Khemka { 260*11b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 261*11b9c3b1SVijay Khemka } 262*11b9c3b1SVijay Khemka 263*11b9c3b1SVijay Khemka /* Identify the next SEL record ID. If recordID is same as 264*11b9c3b1SVijay Khemka * total SeL count then next id should be last entry else 265*11b9c3b1SVijay Khemka * it should be incremented by 1 to current RecordID 266*11b9c3b1SVijay Khemka */ 267*11b9c3b1SVijay Khemka uint16_t nextRecord; 268*11b9c3b1SVijay Khemka if (reqData->recordID == selCnt) 269*11b9c3b1SVijay Khemka { 270*11b9c3b1SVijay Khemka nextRecord = fb_oem::ipmi::sel::lastEntry; 271*11b9c3b1SVijay Khemka } 272*11b9c3b1SVijay Khemka else 273*11b9c3b1SVijay Khemka { 274*11b9c3b1SVijay Khemka nextRecord = reqData->recordID + 1; 275*11b9c3b1SVijay Khemka } 276*11b9c3b1SVijay Khemka 277*11b9c3b1SVijay Khemka if (reqData->readLen == fb_oem::ipmi::sel::entireRecord) 278*11b9c3b1SVijay Khemka { 279*11b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recDataBytes); 280*11b9c3b1SVijay Khemka } 281*11b9c3b1SVijay Khemka else 282*11b9c3b1SVijay Khemka { 283*11b9c3b1SVijay Khemka if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize || 284*11b9c3b1SVijay Khemka reqData->readLen > fb_oem::ipmi::sel::selRecordSize) 285*11b9c3b1SVijay Khemka { 286*11b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 287*11b9c3b1SVijay Khemka } 288*11b9c3b1SVijay Khemka std::vector<uint8_t> recPartData; 289*11b9c3b1SVijay Khemka 290*11b9c3b1SVijay Khemka auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset; 291*11b9c3b1SVijay Khemka auto readLength = std::min(diff, static_cast<int>(reqData->readLen)); 292*11b9c3b1SVijay Khemka 293*11b9c3b1SVijay Khemka for (int i = 0; i < readLength; i++) 294*11b9c3b1SVijay Khemka { 295*11b9c3b1SVijay Khemka recPartData.push_back(recDataBytes[i + reqData->offset]); 296*11b9c3b1SVijay Khemka } 297*11b9c3b1SVijay Khemka return ipmi::responseSuccess(nextRecord, recPartData); 298*11b9c3b1SVijay Khemka } 299*11b9c3b1SVijay Khemka } 300*11b9c3b1SVijay Khemka 301*11b9c3b1SVijay Khemka ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(std::vector<uint8_t> data) 302*11b9c3b1SVijay Khemka { 303*11b9c3b1SVijay Khemka /* Per the IPMI spec, need to cancel any reservation when a 304*11b9c3b1SVijay Khemka * SEL entry is added 305*11b9c3b1SVijay Khemka */ 306*11b9c3b1SVijay Khemka cancelSELReservation(); 307*11b9c3b1SVijay Khemka 308*11b9c3b1SVijay Khemka if (data.size() != fb_oem::ipmi::sel::selRecordSize) 309*11b9c3b1SVijay Khemka { 310*11b9c3b1SVijay Khemka return ipmi::responseReqDataLenInvalid(); 311*11b9c3b1SVijay Khemka } 312*11b9c3b1SVijay Khemka 313*11b9c3b1SVijay Khemka std::string ipmiRaw, logErr; 314*11b9c3b1SVijay Khemka toHexStr(data, ipmiRaw); 315*11b9c3b1SVijay Khemka 316*11b9c3b1SVijay Khemka /* Log the Raw SEL message to the journal */ 317*11b9c3b1SVijay Khemka std::string journalMsg = "SEL Entry Added: " + ipmiRaw; 318*11b9c3b1SVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(journalMsg.c_str()); 319*11b9c3b1SVijay Khemka 320*11b9c3b1SVijay Khemka int responseID = selObj.addEntry(ipmiRaw.c_str()); 321*11b9c3b1SVijay Khemka if (responseID < 0) 322*11b9c3b1SVijay Khemka { 323*11b9c3b1SVijay Khemka return ipmi::responseUnspecifiedError(); 324*11b9c3b1SVijay Khemka } 325*11b9c3b1SVijay Khemka return ipmi::responseSuccess((uint16_t)responseID); 326*11b9c3b1SVijay Khemka } 327*11b9c3b1SVijay Khemka 328*11b9c3b1SVijay Khemka void registerSELFunctions() 329*11b9c3b1SVijay Khemka { 330*11b9c3b1SVijay Khemka // <Get SEL Info> 331*11b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 332*11b9c3b1SVijay Khemka ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 333*11b9c3b1SVijay Khemka ipmiStorageGetSELInfo); 334*11b9c3b1SVijay Khemka 335*11b9c3b1SVijay Khemka // <Get SEL Entry> 336*11b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 337*11b9c3b1SVijay Khemka ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 338*11b9c3b1SVijay Khemka ipmiStorageGetSELEntry); 339*11b9c3b1SVijay Khemka 340*11b9c3b1SVijay Khemka // <Add SEL Entry> 341*11b9c3b1SVijay Khemka ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 342*11b9c3b1SVijay Khemka ipmi::storage::cmdAddSelEntry, 343*11b9c3b1SVijay Khemka ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 344*11b9c3b1SVijay Khemka 345*11b9c3b1SVijay Khemka return; 346*11b9c3b1SVijay Khemka } 347*11b9c3b1SVijay Khemka 348*11b9c3b1SVijay Khemka } // namespace storage 349*11b9c3b1SVijay Khemka } // namespace ipmi 350