blob: ae98ba8ad0d0fd5804d841e5b33b95e50fc42b34 [file] [log] [blame]
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001/*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <ipmid/api.hpp>
19
20#include <boost/algorithm/string/join.hpp>
21#include <nlohmann/json.hpp>
22#include <iostream>
23#include <sstream>
24#include <fstream>
25#include <phosphor-logging/log.hpp>
26#include <sdbusplus/message/types.hpp>
27#include <sdbusplus/timer.hpp>
28#include <storagecommands.hpp>
29
30//----------------------------------------------------------------------
31// Platform specific functions for storing app data
32//----------------------------------------------------------------------
33
34static void toHexStr(std::vector<uint8_t> &bytes, std::string &hexStr)
35{
36 std::stringstream stream;
37 stream << std::hex << std::uppercase << std::setfill('0');
38 for (const uint8_t byte : bytes)
39 {
40 stream << std::setw(2) << static_cast<int>(byte);
41 }
42 hexStr = stream.str();
43}
44
45static int fromHexStr(const std::string hexStr, std::vector<uint8_t> &data)
46{
47 for (unsigned int i = 0; i < hexStr.size(); i += 2)
48 {
49 try
50 {
51 data.push_back(static_cast<uint8_t>(
52 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
53 }
54 catch (std::invalid_argument &e)
55 {
56 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
57 return -1;
58 }
59 catch (std::out_of_range &e)
60 {
61 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
62 return -1;
63 }
64 }
65 return 0;
66}
67
68namespace fb_oem::ipmi::sel
69{
70
71class SELData
72{
73 private:
74 nlohmann::json selDataObj;
75
76 void flush()
77 {
78 std::ofstream file(SEL_JSON_DATA_FILE);
79 file << selDataObj;
80 file.close();
81 }
82
83 void init()
84 {
85 selDataObj[KEY_SEL_VER] = 0x51;
86 selDataObj[KEY_SEL_COUNT] = 0;
87 selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF;
88 selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF;
89 selDataObj[KEY_OPER_SUPP] = 0x02;
90 /* Spec indicates that more than 64kB is free */
91 selDataObj[KEY_FREE_SPACE] = 0xFFFF;
92 }
93
94 public:
95 SELData()
96 {
97 /* Get App data stored in json file */
98 std::ifstream file(SEL_JSON_DATA_FILE);
99 if (file)
100 {
101 file >> selDataObj;
102 file.close();
103 }
104
105 /* Initialize SelData object if no entries. */
106 if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end())
107 {
108 init();
109 }
110 }
111
112 int clear()
113 {
114 /* Clear the complete Sel Json object */
115 selDataObj.clear();
116 /* Reinitialize it with basic data */
117 init();
118 /* Save the erase time */
119 struct timespec selTime = {};
120 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
121 {
122 return -1;
123 }
124 selDataObj[KEY_ERASE_TIME] = selTime.tv_sec;
125 flush();
126 return 0;
127 }
128
129 uint32_t getCount()
130 {
131 return selDataObj[KEY_SEL_COUNT];
132 }
133
134 void getInfo(GetSELInfoData &info)
135 {
136 info.selVersion = selDataObj[KEY_SEL_VER];
137 info.entries = selDataObj[KEY_SEL_COUNT];
138 info.freeSpace = selDataObj[KEY_FREE_SPACE];
139 info.addTimeStamp = selDataObj[KEY_ADD_TIME];
140 info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME];
141 info.operationSupport = selDataObj[KEY_OPER_SUPP];
142 }
143
144 int getEntry(uint32_t index, std::string &rawStr)
145 {
146 std::stringstream ss;
147 ss << std::hex;
148 ss << std::setw(2) << std::setfill('0') << index;
149
150 /* Check or the requested SEL Entry, if record is available */
151 if (selDataObj.find(ss.str()) == selDataObj.end())
152 {
153 return -1;
154 }
155
156 rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW];
157 return 0;
158 }
159
160 int addEntry(std::string keyStr)
161 {
162 struct timespec selTime = {};
163
164 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
165 {
166 return -1;
167 }
168
169 selDataObj[KEY_ADD_TIME] = selTime.tv_sec;
170
171 int selCount = selDataObj[KEY_SEL_COUNT];
172 selDataObj[KEY_SEL_COUNT] = ++selCount;
173
174 std::stringstream ss;
175 ss << std::hex;
176 ss << std::setw(2) << std::setfill('0') << selCount;
177
178 selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr;
179 flush();
180 return selCount;
181 }
182};
183
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700184static void parseStdSel(StdSELEntry *data, std::string &errStr)
185{
186 std::stringstream tmpStream;
187 tmpStream << std::hex << std::uppercase;
188
189 /* TODO: add pal_add_cri_sel */
190 switch (data->sensorNum)
191 {
192 case memoryEccError:
193 switch (data->eventData1 & 0x0F)
194 {
195 case 0x00:
196 errStr = "Correctable";
197 tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
198 << data->eventData3 << " ECC err";
199 break;
200 case 0x01:
201 errStr = "Uncorrectable";
202 tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
203 << data->eventData3 << " UECC err";
204 break;
205 case 0x02:
206 errStr = "Parity";
207 break;
208 case 0x05:
209 errStr = "Correctable ECC error Logging Limit Reached";
210 break;
211 default:
212 errStr = "Unknown";
213 }
214 break;
215 case memoryErrLogDIS:
216 if ((data->eventData1 & 0x0F) == 0)
217 {
218 errStr = "Correctable Memory Error Logging Disabled";
219 }
220 else
221 {
222 errStr = "Unknown";
223 }
224 break;
225 default:
226
227 /* TODO: parse sel helper */
228 errStr = "Unknown";
229 return;
230 }
231
232 errStr += " (DIMM " + std::to_string(data->eventData3) + ")";
233 errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03);
234
235 switch ((data->eventData2 & 0x0C) >> 2)
236 {
237 case 0x00:
238 // Ignore when " All info available"
239 break;
240 case 0x01:
241 errStr += " DIMM info not valid";
242 break;
243 case 0x02:
244 errStr += " CHN info not valid";
245 break;
246 case 0x03:
247 errStr += " CPU info not valid";
248 break;
249 default:
250 errStr += " Unknown";
251 }
252
253 if (((data->eventType & 0x80) >> 7) == 0)
254 {
255 errStr += " Assertion";
256 }
257 else
258 {
259 errStr += " Deassertion";
260 }
261
262 return;
263}
264
265static void parseOemSel(TsOemSELEntry *data, std::string &errStr)
266{
267 std::stringstream tmpStream;
268 tmpStream << std::hex << std::uppercase << std::setfill('0');
269
270 switch (data->recordType)
271 {
272 case 0xC0:
273 tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1]
274 << std::setw(2) << (int)data->oemData[0] << " DID:0x"
275 << std::setw(2) << (int)data->oemData[3] << std::setw(2)
276 << (int)data->oemData[2] << " Slot:0x" << std::setw(2)
277 << (int)data->oemData[4] << " Error ID:0x" << std::setw(2)
278 << (int)data->oemData[5];
279 break;
280 case 0xC2:
281 tmpStream << "Extra info:0x" << std::setw(2)
282 << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2)
283 << (int)data->oemData[3] << std::setw(2)
284 << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2)
285 << (int)data->oemData[5] << std::setw(2)
286 << (int)data->oemData[4];
287 break;
288 case 0xC3:
289 int bank = (data->oemData[1] & 0xf0) >> 4;
290 int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2];
291
292 tmpStream << "Fail Device:0x" << std::setw(2)
293 << (int)data->oemData[0] << " Bank:0x" << std::setw(2)
294 << bank << " Column:0x" << std::setw(2) << col
295 << " Failed Row:0x" << std::setw(2)
296 << (int)data->oemData[3] << std::setw(2)
297 << (int)data->oemData[4] << std::setw(2)
298 << (int)data->oemData[5];
299 }
300
301 errStr = tmpStream.str();
302
303 return;
304}
305
Vijay Khemka34a875f2019-08-09 15:08:15 -0700306static void parseOemUnifiedSel(NtsOemSELEntry *data, std::string &errStr)
307{
308 uint8_t *ptr = data->oemData;
309 int genInfo = ptr[0];
310 int errType = genInfo & 0x0f;
311 std::vector<std::string> dimmEvent = {
312 "Memory training failure", "Memory correctable error",
313 "Memory uncorrectable error", "Reserved"};
314
315 std::stringstream tmpStream;
316 tmpStream << std::hex << std::uppercase << std::setfill('0');
317
318 switch (errType)
319 {
320 case unifiedPcieErr:
321 if (((genInfo & 0x10) >> 4) == 0) // x86
322 {
323 tmpStream << "GeneralInfo: x86/PCIeErr(0x" << std::setw(2)
324 << genInfo << "),";
325 }
326
327 tmpStream << " Bus " << std::setw(2) << (int)(ptr[8]) << "/Dev "
328 << std::setw(2) << (int)(ptr[7] >> 3) << "/Fun "
329 << std::setw(2) << (int)(ptr[7] & 0x7)
330 << ", TotalErrID1Cnt: 0x" << std::setw(4)
331 << (int)((ptr[10] << 8) | ptr[9]) << ", ErrID2: 0x"
332 << std::setw(2) << (int)(ptr[11]) << ", ErrID1: 0x"
333 << std::setw(2) << (int)(ptr[12]);
334
335 break;
336 case unifiedMemErr:
337 tmpStream << "GeneralInfo: MemErr(0x" << std::setw(2) << genInfo
338 << "), DIMM Slot Location: Sled " << std::setw(2)
339 << (int)((ptr[5] >> 4) & 0x03) << "/Socket "
340 << std::setw(2) << (int)(ptr[5] & 0x0f) << ", Channel "
341 << std::setw(2) << (int)(ptr[6] & 0x0f) << ", Slot "
342 << std::setw(2) << (int)(ptr[7] & 0x0f)
343 << ", DIMM Failure Event: " << dimmEvent[(ptr[9] & 0x03)]
344 << ", Major Code: 0x" << std::setw(2) << (int)(ptr[10])
345 << ", Minor Code: 0x" << std::setw(2) << (int)(ptr[11]);
346
347 break;
348 default:
349 std::vector<uint8_t> oemData(ptr, ptr + 13);
350 std::string oemDataStr;
351 toHexStr(oemData, oemDataStr);
352 tmpStream << "Undefined Error Type(0x" << std::setw(2) << errType
353 << "), Raw: " << oemDataStr;
354 }
355
356 errStr = tmpStream.str();
357
358 return;
359}
360
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700361static void parseSelData(std::vector<uint8_t> &reqData, std::string &msgLog)
362{
363
364 /* Get record type */
365 int recType = reqData[2];
366 std::string errType, errLog;
367
368 uint8_t *ptr = NULL;
369
370 std::stringstream recTypeStream;
371 recTypeStream << std::hex << std::uppercase << std::setfill('0')
372 << std::setw(2) << recType;
373
374 msgLog = "SEL Entry: FRU: 1, Record: ";
375
376 if (recType == stdErrType)
377 {
378 StdSELEntry *data = reinterpret_cast<StdSELEntry *>(&reqData[0]);
379 std::string sensorName;
380
381 errType = stdErr;
382 if (data->sensorType == 0x1F)
383 {
384 sensorName = "OS";
385 }
386 else
387 {
388 auto findSensorName = sensorNameTable.find(data->sensorNum);
389 if (findSensorName == sensorNameTable.end())
390 {
391 sensorName = "Unknown";
392 }
393 else
394 {
395 sensorName = findSensorName->second;
396 }
397 }
398
399 std::tm *ts = localtime((time_t *)(&(data->timeStamp)));
400 std::string timeStr = std::asctime(ts);
401
402 parseStdSel(data, errLog);
403 ptr = &(data->eventData1);
404 std::vector<uint8_t> evtData(ptr, ptr + 3);
405 std::string eventData;
406 toHexStr(evtData, eventData);
407
408 std::stringstream senNumStream;
409 senNumStream << std::hex << std::uppercase << std::setfill('0')
410 << std::setw(2) << (int)(data->sensorNum);
411
412 msgLog += errType + " (0x" + recTypeStream.str() +
413 "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" +
414 senNumStream.str() + "), Event Data: (" + eventData + ") " +
415 errLog;
416 }
417 else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
418 {
419 /* timestamped OEM SEL records */
420 TsOemSELEntry *data = reinterpret_cast<TsOemSELEntry *>(&reqData[0]);
421 ptr = data->mfrId;
422 std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
423 std::string mfrIdStr;
424 toHexStr(mfrIdData, mfrIdStr);
425
426 ptr = data->oemData;
427 std::vector<uint8_t> oemData(ptr, ptr + 6);
428 std::string oemDataStr;
429 toHexStr(oemData, oemDataStr);
430
431 std::tm *ts = localtime((time_t *)(&(data->timeStamp)));
432 std::string timeStr = std::asctime(ts);
433
434 errType = oemTSErr;
435 parseOemSel(data, errLog);
436
437 msgLog += errType + " (0x" + recTypeStream.str() +
438 "), Time: " + timeStr + ", MFG ID: " + mfrIdStr +
439 ", OEM Data: (" + oemDataStr + ") " + errLog;
440 }
Vijay Khemka34a875f2019-08-09 15:08:15 -0700441 else if (recType == fbUniErrType)
442 {
443 NtsOemSELEntry *data = reinterpret_cast<NtsOemSELEntry *>(&reqData[0]);
444 errType = fbUniSELErr;
445 parseOemUnifiedSel(data, errLog);
446 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
447 }
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700448 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
449 {
450 /* Non timestamped OEM SEL records */
451 NtsOemSELEntry *data = reinterpret_cast<NtsOemSELEntry *>(&reqData[0]);
452 errType = oemNTSErr;
453
454 ptr = data->oemData;
455 std::vector<uint8_t> oemData(ptr, ptr + 13);
456 std::string oemDataStr;
457 toHexStr(oemData, oemDataStr);
458
459 parseOemSel((TsOemSELEntry *)data, errLog);
460 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
461 oemDataStr + ") " + errLog;
462 }
463 else
464 {
465 errType = unknownErr;
466 toHexStr(reqData, errLog);
467 msgLog +=
468 errType + " (0x" + recTypeStream.str() + ") RawData: " + errLog;
469 }
470}
471
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700472} // namespace fb_oem::ipmi::sel
473
474namespace ipmi
475{
476
477namespace storage
478{
479
480static void registerSELFunctions() __attribute__((constructor));
481static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
482
483ipmi::RspType<uint8_t, // SEL version
484 uint16_t, // SEL entry count
485 uint16_t, // free space
486 uint32_t, // last add timestamp
487 uint32_t, // last erase timestamp
488 uint8_t> // operation support
489 ipmiStorageGetSELInfo()
490{
491
492 fb_oem::ipmi::sel::GetSELInfoData info;
493
494 selObj.getInfo(info);
495 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
496 info.addTimeStamp, info.eraseTimeStamp,
497 info.operationSupport);
498}
499
500ipmi::RspType<uint16_t, std::vector<uint8_t>>
501 ipmiStorageGetSELEntry(std::vector<uint8_t> data)
502{
503
504 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
505 {
506 return ipmi::responseReqDataLenInvalid();
507 }
508
509 fb_oem::ipmi::sel::GetSELEntryRequest *reqData =
510 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest *>(&data[0]);
511
512 if (reqData->reservID != 0)
513 {
514 if (!checkSELReservation(reqData->reservID))
515 {
516 return ipmi::responseInvalidReservationId();
517 }
518 }
519
520 uint16_t selCnt = selObj.getCount();
521 if (selCnt == 0)
522 {
523 return ipmi::responseSensorInvalid();
524 }
525
526 /* If it is asked for first entry */
527 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
528 {
529 /* First Entry (0x0000) as per Spec */
530 reqData->recordID = 1;
531 }
532 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
533 {
534 /* Last entry (0xFFFF) as per Spec */
535 reqData->recordID = selCnt;
536 }
537
538 std::string ipmiRaw;
539
540 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
541 {
542 return ipmi::responseSensorInvalid();
543 }
544
545 std::vector<uint8_t> recDataBytes;
546 if (fromHexStr(ipmiRaw, recDataBytes) < 0)
547 {
548 return ipmi::responseUnspecifiedError();
549 }
550
551 /* Identify the next SEL record ID. If recordID is same as
552 * total SeL count then next id should be last entry else
553 * it should be incremented by 1 to current RecordID
554 */
555 uint16_t nextRecord;
556 if (reqData->recordID == selCnt)
557 {
558 nextRecord = fb_oem::ipmi::sel::lastEntry;
559 }
560 else
561 {
562 nextRecord = reqData->recordID + 1;
563 }
564
565 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
566 {
567 return ipmi::responseSuccess(nextRecord, recDataBytes);
568 }
569 else
570 {
571 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
572 reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
573 {
574 return ipmi::responseUnspecifiedError();
575 }
576 std::vector<uint8_t> recPartData;
577
578 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
579 auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
580
581 for (int i = 0; i < readLength; i++)
582 {
583 recPartData.push_back(recDataBytes[i + reqData->offset]);
584 }
585 return ipmi::responseSuccess(nextRecord, recPartData);
586 }
587}
588
589ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(std::vector<uint8_t> data)
590{
591 /* Per the IPMI spec, need to cancel any reservation when a
592 * SEL entry is added
593 */
594 cancelSELReservation();
595
596 if (data.size() != fb_oem::ipmi::sel::selRecordSize)
597 {
598 return ipmi::responseReqDataLenInvalid();
599 }
600
601 std::string ipmiRaw, logErr;
602 toHexStr(data, ipmiRaw);
603
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700604 /* Parse sel data and get an error log to be filed */
605 fb_oem::ipmi::sel::parseSelData(data, logErr);
606
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700607 /* Log the Raw SEL message to the journal */
608 std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700609
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700610 phosphor::logging::log<phosphor::logging::level::INFO>(journalMsg.c_str());
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700611 phosphor::logging::log<phosphor::logging::level::INFO>(logErr.c_str());
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700612
613 int responseID = selObj.addEntry(ipmiRaw.c_str());
614 if (responseID < 0)
615 {
616 return ipmi::responseUnspecifiedError();
617 }
618 return ipmi::responseSuccess((uint16_t)responseID);
619}
620
Vijay Khemkac1921c62019-08-09 13:11:31 -0700621ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
622 const std::array<uint8_t, 3> &clr,
623 uint8_t eraseOperation)
624{
625 if (!checkSELReservation(reservationID))
626 {
627 return ipmi::responseInvalidReservationId();
628 }
629
630 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
631 if (clr != clrExpected)
632 {
633 return ipmi::responseInvalidFieldRequest();
634 }
635
636 /* If there is no sel then return erase complete */
637 if (selObj.getCount() == 0)
638 {
639 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
640 }
641
642 /* Erasure status cannot be fetched, so always return erasure
643 * status as `erase completed`.
644 */
645 if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
646 {
647 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
648 }
649
650 /* Check that initiate erase is correct */
651 if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
652 {
653 return ipmi::responseInvalidFieldRequest();
654 }
655
656 /* Per the IPMI spec, need to cancel any reservation when the
657 * SEL is cleared
658 */
659 cancelSELReservation();
660
661 /* Clear the complete Sel Json object */
662 if (selObj.clear() < 0)
663 {
664 return ipmi::responseUnspecifiedError();
665 }
666
667 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
668}
669
670ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
671{
672 struct timespec selTime = {};
673
674 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
675 {
676 return ipmi::responseUnspecifiedError();
677 }
678
679 return ipmi::responseSuccess(selTime.tv_sec);
680}
681
682ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime)
683{
684 // Set SEL Time is not supported
685 return ipmi::responseInvalidCommand();
686}
687
688ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
689{
690 /* TODO: For now, the SEL time stamp is based on UTC time,
691 * so return 0x0000 as offset. Might need to change once
692 * supporting zones in SEL time stamps
693 */
694
695 uint16_t utcOffset = 0x0000;
696 return ipmi::responseSuccess(utcOffset);
697}
698
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700699void registerSELFunctions()
700{
701 // <Get SEL Info>
702 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
703 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
704 ipmiStorageGetSELInfo);
705
706 // <Get SEL Entry>
707 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
708 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
709 ipmiStorageGetSELEntry);
710
711 // <Add SEL Entry>
712 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
713 ipmi::storage::cmdAddSelEntry,
714 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
715
Vijay Khemkac1921c62019-08-09 13:11:31 -0700716 // <Clear SEL>
717 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
718 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
719 ipmiStorageClearSEL);
720
721 // <Get SEL Time>
722 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
723 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
724 ipmiStorageGetSELTime);
725
726 // <Set SEL Time>
727 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
728 ipmi::storage::cmdSetSelTime,
729 ipmi::Privilege::Operator, ipmiStorageSetSELTime);
730
731 // <Get SEL Time UTC Offset>
732 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
733 ipmi::storage::cmdGetSelTimeUtcOffset,
734 ipmi::Privilege::User,
735 ipmiStorageGetSELTimeUtcOffset);
736
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700737 return;
738}
739
740} // namespace storage
741} // namespace ipmi