xref: /openbmc/fb-ipmi-oem/src/selcommands.cpp (revision f36f345fe23df71b66fd184971d73585a841b1df)
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