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