xref: /openbmc/fb-ipmi-oem/src/oemcommands.cpp (revision d1194024)
1 /*
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 "xyz/openbmc_project/Common/error.hpp"
19 
20 #include <ipmid/api.hpp>
21 #include <ipmid/utils.hpp>
22 #include <commandutils.hpp>
23 #include <nlohmann/json.hpp>
24 #include <oemcommands.hpp>
25 #include <phosphor-logging/log.hpp>
26 #include <sdbusplus/bus.hpp>
27 
28 #include <array>
29 #include <cstring>
30 #include <fstream>
31 #include <iomanip>
32 #include <iostream>
33 #include <sstream>
34 #include <string>
35 #include <vector>
36 
37 #define SIZE_IANA_ID 3
38 
39 namespace ipmi
40 {
41 
42 using namespace phosphor::logging;
43 
44 static void registerOEMFunctions() __attribute__((constructor));
45 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
46 static constexpr size_t maxFRUStringLength = 0x3F;
47 
48 int plat_udbg_get_post_desc(uint8_t, uint8_t*, uint8_t, uint8_t*, uint8_t*,
49                             uint8_t*);
50 int plat_udbg_get_gpio_desc(uint8_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*,
51                             uint8_t*);
52 ipmi_ret_t plat_udbg_get_frame_data(uint8_t, uint8_t, uint8_t*, uint8_t*,
53                                     uint8_t*);
54 ipmi_ret_t plat_udbg_control_panel(uint8_t, uint8_t, uint8_t, uint8_t*,
55                                    uint8_t*);
56 int sendMeCmd(uint8_t, uint8_t, std::vector<uint8_t>&, std::vector<uint8_t>&);
57 
58 nlohmann::json oemData __attribute__((init_priority(101)));
59 
60 static constexpr size_t GUID_SIZE = 16;
61 // TODO Make offset and location runtime configurable to ensure we
62 // can make each define their own locations.
63 static constexpr off_t OFFSET_SYS_GUID = 0x17F0;
64 static constexpr const char* FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom";
65 
66 enum class LanParam : uint8_t
67 {
68     INPROGRESS = 0,
69     AUTHSUPPORT = 1,
70     AUTHENABLES = 2,
71     IP = 3,
72     IPSRC = 4,
73     MAC = 5,
74     SUBNET = 6,
75     GATEWAY = 12,
76     VLAN = 20,
77     CIPHER_SUITE_COUNT = 22,
78     CIPHER_SUITE_ENTRIES = 23,
79     IPV6 = 59,
80 };
81 
82 namespace network
83 {
84 
85 constexpr auto ROOT = "/xyz/openbmc_project/network";
86 constexpr auto SERVICE = "xyz.openbmc_project.Network";
87 constexpr auto IPV4_TYPE = "ipv4";
88 constexpr auto IPV6_TYPE = "ipv6";
89 constexpr auto IPV4_PREFIX = "169.254";
90 constexpr auto IPV6_PREFIX = "fe80";
91 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
92 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
93 
94 bool isLinkLocalIP(const std::string& address)
95 {
96     return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
97 }
98 
99 DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
100                            const std::string& interface,
101                            const std::string& serviceRoot,
102                            const std::string& match)
103 {
104     auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
105 
106     if (objectTree.empty())
107     {
108         log<level::ERR>("No Object has implemented the IP interface",
109                         entry("INTERFACE=%s", interface.c_str()));
110     }
111 
112     DbusObjectInfo objectInfo;
113 
114     for (auto& object : objectTree)
115     {
116         auto variant =
117             ipmi::getDbusProperty(bus, object.second.begin()->first,
118                                   object.first, IP_INTERFACE, "Address");
119 
120         objectInfo = std::make_pair(object.first, object.second.begin()->first);
121 
122         // if LinkLocalIP found look for Non-LinkLocalIP
123         if (isLinkLocalIP(std::get<std::string>(variant)))
124         {
125             continue;
126         }
127         else
128         {
129             break;
130         }
131     }
132     return objectInfo;
133 }
134 
135 } // namespace network
136 
137 //----------------------------------------------------------------------
138 // Helper functions for storing oem data
139 //----------------------------------------------------------------------
140 
141 void flushOemData()
142 {
143     std::ofstream file(JSON_OEM_DATA_FILE);
144     file << oemData;
145     file.close();
146     return;
147 }
148 
149 std::string bytesToStr(uint8_t* byte, int len)
150 {
151     std::stringstream ss;
152     int i;
153 
154     ss << std::hex;
155     for (i = 0; i < len; i++)
156     {
157         ss << std::setw(2) << std::setfill('0') << (int)byte[i];
158     }
159 
160     return ss.str();
161 }
162 
163 int strToBytes(std::string& str, uint8_t* data)
164 {
165     std::string sstr;
166     int i;
167 
168     for (i = 0; i < (str.length()) / 2; i++)
169     {
170         sstr = str.substr(i * 2, 2);
171         data[i] = (uint8_t)std::strtol(sstr.c_str(), NULL, 16);
172     }
173     return i;
174 }
175 
176 ipmi_ret_t getNetworkData(uint8_t lan_param, char* data)
177 {
178     ipmi_ret_t rc = IPMI_CC_OK;
179     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
180 
181     const std::string ethdevice = "eth0";
182 
183     switch (static_cast<LanParam>(lan_param))
184     {
185         case LanParam::IP:
186         {
187             auto ethIP = ethdevice + "/" + ipmi::network::IPV4_TYPE;
188             std::string ipaddress;
189             auto ipObjectInfo = ipmi::network::getIPObject(
190                 bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT, ethIP);
191 
192             auto properties = ipmi::getAllDbusProperties(
193                 bus, ipObjectInfo.second, ipObjectInfo.first,
194                 ipmi::network::IP_INTERFACE);
195 
196             ipaddress = std::get<std::string>(properties["Address"]);
197 
198             std::strcpy(data, ipaddress.c_str());
199         }
200         break;
201 
202         case LanParam::IPV6:
203         {
204             auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE;
205             std::string ipaddress;
206             auto ipObjectInfo = ipmi::network::getIPObject(
207                 bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT, ethIP);
208 
209             auto properties = ipmi::getAllDbusProperties(
210                 bus, ipObjectInfo.second, ipObjectInfo.first,
211                 ipmi::network::IP_INTERFACE);
212 
213             ipaddress = std::get<std::string>(properties["Address"]);
214 
215             std::strcpy(data, ipaddress.c_str());
216         }
217         break;
218 
219         case LanParam::MAC:
220         {
221             std::string macAddress;
222             auto macObjectInfo =
223                 ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
224                                     ipmi::network::ROOT, ethdevice);
225 
226             auto variant = ipmi::getDbusProperty(
227                 bus, macObjectInfo.second, macObjectInfo.first,
228                 ipmi::network::MAC_INTERFACE, "MACAddress");
229 
230             macAddress = std::get<std::string>(variant);
231 
232             sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
233                    (data), (data + 1), (data + 2), (data + 3), (data + 4),
234                    (data + 5));
235             std::strcpy(data, macAddress.c_str());
236         }
237         break;
238 
239         default:
240             rc = IPMI_CC_PARM_OUT_OF_RANGE;
241     }
242     return rc;
243 }
244 
245 // return code: 0 successful
246 int8_t getFruData(std::string& data, std::string& name)
247 {
248     std::string objpath = "/xyz/openbmc_project/FruDevice";
249     std::string intf = "xyz.openbmc_project.FruDeviceManager";
250     std::string service = getService(dbus, intf, objpath);
251     ObjectValueTree valueTree = getManagedObjects(dbus, service, "/");
252     if (valueTree.empty())
253     {
254         phosphor::logging::log<phosphor::logging::level::ERR>(
255             "No object implements interface",
256             phosphor::logging::entry("INTF=%s", intf.c_str()));
257         return -1;
258     }
259 
260     for (const auto& item : valueTree)
261     {
262         auto interface = item.second.find("xyz.openbmc_project.FruDevice");
263         if (interface == item.second.end())
264         {
265             continue;
266         }
267 
268         auto property = interface->second.find(name.c_str());
269         if (property == interface->second.end())
270         {
271             continue;
272         }
273 
274         try
275         {
276             Value variant = property->second;
277             std::string& result = std::get<std::string>(variant);
278             if (result.size() > maxFRUStringLength)
279             {
280                 phosphor::logging::log<phosphor::logging::level::ERR>(
281                     "FRU serial number exceed maximum length");
282                 return -1;
283             }
284             data = result;
285             return 0;
286         }
287         catch (std::bad_variant_access& e)
288         {
289             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
290             return -1;
291         }
292     }
293     return -1;
294 }
295 
296 typedef struct
297 {
298     uint8_t cur_power_state;
299     uint8_t last_power_event;
300     uint8_t misc_power_state;
301     uint8_t front_panel_button_cap_status;
302 } ipmi_get_chassis_status_t;
303 
304 //----------------------------------------------------------------------
305 // Get Debug Frame Info
306 //----------------------------------------------------------------------
307 ipmi_ret_t ipmiOemDbgGetFrameInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
308                                   ipmi_request_t request,
309                                   ipmi_response_t response,
310                                   ipmi_data_len_t data_len,
311                                   ipmi_context_t context)
312 {
313     uint8_t* req = reinterpret_cast<uint8_t*>(request);
314     uint8_t* res = reinterpret_cast<uint8_t*>(response);
315     uint8_t num_frames = 3;
316 
317     std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
318     res[SIZE_IANA_ID] = num_frames;
319     *data_len = SIZE_IANA_ID + 1;
320 
321     return IPMI_CC_OK;
322 }
323 
324 //----------------------------------------------------------------------
325 // Get Debug Updated Frames
326 //----------------------------------------------------------------------
327 ipmi_ret_t ipmiOemDbgGetUpdFrames(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
328                                   ipmi_request_t request,
329                                   ipmi_response_t response,
330                                   ipmi_data_len_t data_len,
331                                   ipmi_context_t context)
332 {
333     uint8_t* req = reinterpret_cast<uint8_t*>(request);
334     uint8_t* res = reinterpret_cast<uint8_t*>(response);
335     uint8_t num_updates = 3;
336     *data_len = 4;
337 
338     std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
339     res[SIZE_IANA_ID] = num_updates;
340     *data_len = SIZE_IANA_ID + num_updates + 1;
341     res[SIZE_IANA_ID + 1] = 1; // info page update
342     res[SIZE_IANA_ID + 2] = 2; // cri sel update
343     res[SIZE_IANA_ID + 3] = 3; // cri sensor update
344 
345     return IPMI_CC_OK;
346 }
347 
348 //----------------------------------------------------------------------
349 // Get Debug POST Description
350 //----------------------------------------------------------------------
351 ipmi_ret_t ipmiOemDbgGetPostDesc(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
352                                  ipmi_request_t request,
353                                  ipmi_response_t response,
354                                  ipmi_data_len_t data_len,
355                                  ipmi_context_t context)
356 {
357     uint8_t* req = reinterpret_cast<uint8_t*>(request);
358     uint8_t* res = reinterpret_cast<uint8_t*>(response);
359     uint8_t index = 0;
360     uint8_t next = 0;
361     uint8_t end = 0;
362     uint8_t phase = 0;
363     uint8_t descLen = 0;
364     int ret;
365 
366     index = req[3];
367     phase = req[4];
368 
369     ret = plat_udbg_get_post_desc(index, &next, phase, &end, &descLen, &res[8]);
370     if (ret)
371     {
372         memcpy(res, req, SIZE_IANA_ID); // IANA ID
373         *data_len = SIZE_IANA_ID;
374         return IPMI_CC_UNSPECIFIED_ERROR;
375     }
376 
377     memcpy(res, req, SIZE_IANA_ID); // IANA ID
378     res[3] = index;
379     res[4] = next;
380     res[5] = phase;
381     res[6] = end;
382     res[7] = descLen;
383     *data_len = SIZE_IANA_ID + 5 + descLen;
384 
385     return IPMI_CC_OK;
386 }
387 
388 //----------------------------------------------------------------------
389 // Get Debug GPIO Description
390 //----------------------------------------------------------------------
391 ipmi_ret_t ipmiOemDbgGetGpioDesc(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
392                                  ipmi_request_t request,
393                                  ipmi_response_t response,
394                                  ipmi_data_len_t data_len,
395                                  ipmi_context_t context)
396 {
397     uint8_t* req = reinterpret_cast<uint8_t*>(request);
398     uint8_t* res = reinterpret_cast<uint8_t*>(response);
399 
400     uint8_t index = 0;
401     uint8_t next = 0;
402     uint8_t level = 0;
403     uint8_t pinDef = 0;
404     uint8_t descLen = 0;
405     int ret;
406 
407     index = req[3];
408 
409     ret = plat_udbg_get_gpio_desc(index, &next, &level, &pinDef, &descLen,
410                                   &res[8]);
411     if (ret)
412     {
413         memcpy(res, req, SIZE_IANA_ID); // IANA ID
414         *data_len = SIZE_IANA_ID;
415         return IPMI_CC_UNSPECIFIED_ERROR;
416     }
417 
418     memcpy(res, req, SIZE_IANA_ID); // IANA ID
419     res[3] = index;
420     res[4] = next;
421     res[5] = level;
422     res[6] = pinDef;
423     res[7] = descLen;
424     *data_len = SIZE_IANA_ID + 5 + descLen;
425 
426     return IPMI_CC_OK;
427 }
428 
429 //----------------------------------------------------------------------
430 // Get Debug Frame Data
431 //----------------------------------------------------------------------
432 ipmi_ret_t ipmiOemDbgGetFrameData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
433                                   ipmi_request_t request,
434                                   ipmi_response_t response,
435                                   ipmi_data_len_t data_len,
436                                   ipmi_context_t context)
437 {
438     uint8_t* req = reinterpret_cast<uint8_t*>(request);
439     uint8_t* res = reinterpret_cast<uint8_t*>(response);
440     uint8_t frame;
441     uint8_t page;
442     uint8_t next;
443     uint8_t count;
444     int ret;
445 
446     frame = req[3];
447     page = req[4];
448     int fr = frame;
449     int pg = page;
450 
451     ret = plat_udbg_get_frame_data(frame, page, &next, &count, &res[7]);
452     if (ret)
453     {
454         memcpy(res, req, SIZE_IANA_ID); // IANA ID
455         *data_len = SIZE_IANA_ID;
456         return IPMI_CC_UNSPECIFIED_ERROR;
457     }
458 
459     memcpy(res, req, SIZE_IANA_ID); // IANA ID
460     res[3] = frame;
461     res[4] = page;
462     res[5] = next;
463     res[6] = count;
464     *data_len = SIZE_IANA_ID + 4 + count;
465 
466     return IPMI_CC_OK;
467 }
468 
469 //----------------------------------------------------------------------
470 // Get Debug Control Panel
471 //----------------------------------------------------------------------
472 ipmi_ret_t ipmiOemDbgGetCtrlPanel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
473                                   ipmi_request_t request,
474                                   ipmi_response_t response,
475                                   ipmi_data_len_t data_len,
476                                   ipmi_context_t context)
477 {
478     uint8_t* req = reinterpret_cast<uint8_t*>(request);
479     uint8_t* res = reinterpret_cast<uint8_t*>(response);
480 
481     uint8_t panel;
482     uint8_t operation;
483     uint8_t item;
484     uint8_t count;
485     ipmi_ret_t ret;
486 
487     panel = req[3];
488     operation = req[4];
489     item = req[5];
490 
491     ret = plat_udbg_control_panel(panel, operation, item, &count, &res[3]);
492 
493     std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
494     *data_len = SIZE_IANA_ID + count;
495 
496     return ret;
497 }
498 
499 //----------------------------------------------------------------------
500 // Set Dimm Info (CMD_OEM_SET_DIMM_INFO)
501 //----------------------------------------------------------------------
502 ipmi_ret_t ipmiOemSetDimmInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
503                               ipmi_request_t request, ipmi_response_t response,
504                               ipmi_data_len_t data_len, ipmi_context_t context)
505 {
506     uint8_t* req = reinterpret_cast<uint8_t*>(request);
507 
508     uint8_t index = req[0];
509     uint8_t type = req[1];
510     uint16_t speed;
511     uint32_t size;
512 
513     memcpy(&speed, &req[2], 2);
514     memcpy(&size, &req[4], 4);
515 
516     std::stringstream ss;
517     ss << std::hex;
518     ss << std::setw(2) << std::setfill('0') << (int)index;
519 
520     oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_INDEX] = index;
521     oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_TYPE] = type;
522     oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_SPEED] = speed;
523     oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_SIZE] = size;
524 
525     flushOemData();
526 
527     *data_len = 0;
528 
529     return IPMI_CC_OK;
530 }
531 
532 //----------------------------------------------------------------------
533 // Get Board ID (CMD_OEM_GET_BOARD_ID)
534 //----------------------------------------------------------------------
535 ipmi_ret_t ipmiOemGetBoardID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
536                              ipmi_request_t request, ipmi_response_t response,
537                              ipmi_data_len_t data_len, ipmi_context_t context)
538 {
539     uint8_t* req = reinterpret_cast<uint8_t*>(request);
540     uint8_t* res = reinterpret_cast<uint8_t*>(response);
541 
542     /* TODO: Needs to implement this after GPIO implementation */
543     *data_len = 0;
544 
545     return IPMI_CC_OK;
546 }
547 
548 /* Helper functions to set boot order */
549 void setBootOrder(uint8_t* data)
550 {
551     nlohmann::json bootMode;
552     uint8_t mode = data[0];
553     int i;
554 
555     bootMode["UEFI"] = (mode & BOOT_MODE_UEFI ? true : false);
556     bootMode["CMOS_CLR"] = (mode & BOOT_MODE_CMOS_CLR ? true : false);
557     bootMode["FORCE_BOOT"] = (mode & BOOT_MODE_FORCE_BOOT ? true : false);
558     bootMode["BOOT_FLAG"] = (mode & BOOT_MODE_BOOT_FLAG ? true : false);
559     oemData[KEY_BOOT_ORDER][KEY_BOOT_MODE] = bootMode;
560 
561     /* Initialize boot sequence array */
562     oemData[KEY_BOOT_ORDER][KEY_BOOT_SEQ] = {};
563     for (i = 1; i < SIZE_BOOT_ORDER; i++)
564     {
565         if (data[i] >= BOOT_SEQ_ARRAY_SIZE)
566             oemData[KEY_BOOT_ORDER][KEY_BOOT_SEQ][i - 1] = "NA";
567         else
568             oemData[KEY_BOOT_ORDER][KEY_BOOT_SEQ][i - 1] = bootSeq[data[i]];
569     }
570 
571     flushOemData();
572 }
573 
574 //----------------------------------------------------------------------
575 // Set Boot Order (CMD_OEM_SET_BOOT_ORDER)
576 //----------------------------------------------------------------------
577 ipmi_ret_t ipmiOemSetBootOrder(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
578                                ipmi_request_t request, ipmi_response_t response,
579                                ipmi_data_len_t data_len, ipmi_context_t context)
580 {
581     uint8_t* req = reinterpret_cast<uint8_t*>(request);
582     uint8_t len = *data_len;
583 
584     *data_len = 0;
585 
586     if (len != SIZE_BOOT_ORDER)
587     {
588         phosphor::logging::log<phosphor::logging::level::ERR>(
589             "Invalid Boot order length received");
590         return IPMI_CC_REQ_DATA_LEN_INVALID;
591     }
592 
593     setBootOrder(req);
594 
595     return IPMI_CC_OK;
596 }
597 
598 //----------------------------------------------------------------------
599 // Get Boot Order (CMD_OEM_GET_BOOT_ORDER)
600 //----------------------------------------------------------------------
601 ipmi_ret_t ipmiOemGetBootOrder(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
602                                ipmi_request_t request, ipmi_response_t response,
603                                ipmi_data_len_t data_len, ipmi_context_t context)
604 {
605     uint8_t* res = reinterpret_cast<uint8_t*>(response);
606     uint8_t mode = 0;
607     int i;
608 
609     *data_len = SIZE_BOOT_ORDER;
610 
611     if (oemData.find(KEY_BOOT_ORDER) == oemData.end())
612     {
613         /* Return default boot order 0100090203ff */
614         uint8_t defaultBoot[SIZE_BOOT_ORDER] = {
615             BOOT_MODE_UEFI,      bootMap["USB_DEV"], bootMap["NET_IPV6"],
616             bootMap["SATA_HDD"], bootMap["SATA_CD"], 0xff};
617 
618         memcpy(res, defaultBoot, SIZE_BOOT_ORDER);
619         phosphor::logging::log<phosphor::logging::level::INFO>(
620             "Set default boot order");
621         setBootOrder(defaultBoot);
622     }
623     else
624     {
625         nlohmann::json bootMode = oemData[KEY_BOOT_ORDER][KEY_BOOT_MODE];
626         if (bootMode["UEFI"])
627             mode |= BOOT_MODE_UEFI;
628         if (bootMode["CMOS_CLR"])
629             mode |= BOOT_MODE_CMOS_CLR;
630         if (bootMode["BOOT_FLAG"])
631             mode |= BOOT_MODE_BOOT_FLAG;
632 
633         res[0] = mode;
634 
635         for (i = 1; i < SIZE_BOOT_ORDER; i++)
636         {
637             std::string seqStr = oemData[KEY_BOOT_ORDER][KEY_BOOT_SEQ][i - 1];
638             if (bootMap.find(seqStr) != bootMap.end())
639                 res[i] = bootMap[seqStr];
640             else
641                 res[i] = 0xff;
642         }
643     }
644 
645     return IPMI_CC_OK;
646 }
647 
648 //----------------------------------------------------------------------
649 // Set Machine Config Info (CMD_OEM_SET_MACHINE_CONFIG_INFO)
650 //----------------------------------------------------------------------
651 ipmi_ret_t ipmiOemSetMachineCfgInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
652                                     ipmi_request_t request,
653                                     ipmi_response_t response,
654                                     ipmi_data_len_t data_len,
655                                     ipmi_context_t context)
656 {
657     machineConfigInfo_t* req = reinterpret_cast<machineConfigInfo_t*>(request);
658     uint8_t len = *data_len;
659 
660     *data_len = 0;
661 
662     if (len < sizeof(machineConfigInfo_t))
663     {
664         phosphor::logging::log<phosphor::logging::level::ERR>(
665             "Invalid machine configuration length received");
666         return IPMI_CC_REQ_DATA_LEN_INVALID;
667     }
668 
669     if (req->chassis_type >= sizeof(chassisType) / sizeof(uint8_t*))
670         oemData[KEY_MC_CONFIG][KEY_MC_CHAS_TYPE] = "UNKNOWN";
671     else
672         oemData[KEY_MC_CONFIG][KEY_MC_CHAS_TYPE] =
673             chassisType[req->chassis_type];
674 
675     if (req->mb_type >= sizeof(mbType) / sizeof(uint8_t*))
676         oemData[KEY_MC_CONFIG][KEY_MC_MB_TYPE] = "UNKNOWN";
677     else
678         oemData[KEY_MC_CONFIG][KEY_MC_MB_TYPE] = mbType[req->mb_type];
679 
680     oemData[KEY_MC_CONFIG][KEY_MC_PROC_CNT] = req->proc_cnt;
681     oemData[KEY_MC_CONFIG][KEY_MC_MEM_CNT] = req->mem_cnt;
682     oemData[KEY_MC_CONFIG][KEY_MC_HDD35_CNT] = req->hdd35_cnt;
683     oemData[KEY_MC_CONFIG][KEY_MC_HDD25_CNT] = req->hdd25_cnt;
684 
685     if (req->riser_type >= sizeof(riserType) / sizeof(uint8_t*))
686         oemData[KEY_MC_CONFIG][KEY_MC_RSR_TYPE] = "UNKNOWN";
687     else
688         oemData[KEY_MC_CONFIG][KEY_MC_RSR_TYPE] = riserType[req->riser_type];
689 
690     oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC] = {};
691     int i = 0;
692     if (req->pcie_card_loc & BIT_0)
693         oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT1";
694     if (req->pcie_card_loc & BIT_1)
695         oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT2";
696     if (req->pcie_card_loc & BIT_2)
697         oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT3";
698     if (req->pcie_card_loc & BIT_3)
699         oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT4";
700 
701     if (req->slot1_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
702         oemData[KEY_MC_CONFIG][KEY_MC_SLOT1_TYPE] = "UNKNOWN";
703     else
704         oemData[KEY_MC_CONFIG][KEY_MC_SLOT1_TYPE] =
705             pcieType[req->slot1_pcie_type];
706 
707     if (req->slot2_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
708         oemData[KEY_MC_CONFIG][KEY_MC_SLOT2_TYPE] = "UNKNOWN";
709     else
710         oemData[KEY_MC_CONFIG][KEY_MC_SLOT2_TYPE] =
711             pcieType[req->slot2_pcie_type];
712 
713     if (req->slot3_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
714         oemData[KEY_MC_CONFIG][KEY_MC_SLOT3_TYPE] = "UNKNOWN";
715     else
716         oemData[KEY_MC_CONFIG][KEY_MC_SLOT3_TYPE] =
717             pcieType[req->slot3_pcie_type];
718 
719     if (req->slot4_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
720         oemData[KEY_MC_CONFIG][KEY_MC_SLOT4_TYPE] = "UNKNOWN";
721     else
722         oemData[KEY_MC_CONFIG][KEY_MC_SLOT4_TYPE] =
723             pcieType[req->slot4_pcie_type];
724 
725     oemData[KEY_MC_CONFIG][KEY_MC_AEP_CNT] = req->aep_mem_cnt;
726 
727     flushOemData();
728 
729     return IPMI_CC_OK;
730 }
731 
732 //----------------------------------------------------------------------
733 // Set POST start (CMD_OEM_SET_POST_START)
734 //----------------------------------------------------------------------
735 ipmi_ret_t ipmiOemSetPostStart(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
736                                ipmi_request_t request, ipmi_response_t response,
737                                ipmi_data_len_t data_len, ipmi_context_t context)
738 {
739     phosphor::logging::log<phosphor::logging::level::INFO>("POST Start Event");
740 
741     /* Do nothing, return success */
742     *data_len = 0;
743     return IPMI_CC_OK;
744 }
745 
746 //----------------------------------------------------------------------
747 // Set POST End (CMD_OEM_SET_POST_END)
748 //----------------------------------------------------------------------
749 ipmi_ret_t ipmiOemSetPostEnd(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
750                              ipmi_request_t request, ipmi_response_t response,
751                              ipmi_data_len_t data_len, ipmi_context_t context)
752 {
753     struct timespec ts;
754 
755     phosphor::logging::log<phosphor::logging::level::INFO>("POST End Event");
756 
757     *data_len = 0;
758 
759     // Timestamp post end time.
760     clock_gettime(CLOCK_REALTIME, &ts);
761     oemData[KEY_TS_SLED] = ts.tv_sec;
762     flushOemData();
763 
764     // Sync time with system
765     // TODO: Add code for syncing time
766 
767     return IPMI_CC_OK;
768 }
769 
770 //----------------------------------------------------------------------
771 // Set PPIN Info (CMD_OEM_SET_PPIN_INFO)
772 //----------------------------------------------------------------------
773 // Inform BMC about PPIN data of 8 bytes for each CPU
774 //
775 // Request:
776 // Byte 1:8 – CPU0 PPIN data
777 // Optional:
778 // Byte 9:16 – CPU1 PPIN data
779 //
780 // Response:
781 // Byte 1 – Completion Code
782 ipmi_ret_t ipmiOemSetPPINInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
783                               ipmi_request_t request, ipmi_response_t response,
784                               ipmi_data_len_t data_len, ipmi_context_t context)
785 {
786     uint8_t* req = reinterpret_cast<uint8_t*>(request);
787     std::string ppinStr;
788     int len;
789 
790     if (*data_len > SIZE_CPU_PPIN * 2)
791         len = SIZE_CPU_PPIN * 2;
792     else
793         len = *data_len;
794     *data_len = 0;
795 
796     ppinStr = bytesToStr(req, len);
797     oemData[KEY_PPIN_INFO] = ppinStr.c_str();
798     flushOemData();
799 
800     return IPMI_CC_OK;
801 }
802 
803 //----------------------------------------------------------------------
804 // Set ADR Trigger (CMD_OEM_SET_ADR_TRIGGER)
805 //----------------------------------------------------------------------
806 ipmi_ret_t ipmiOemSetAdrTrigger(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
807                                 ipmi_request_t request,
808                                 ipmi_response_t response,
809                                 ipmi_data_len_t data_len,
810                                 ipmi_context_t context)
811 {
812     /* Do nothing, return success */
813     *data_len = 0;
814     return IPMI_CC_OK;
815 }
816 
817 // Helper function to set guid at offset in EEPROM
818 static int setGUID(off_t offset, uint8_t* guid)
819 {
820     int fd = -1;
821     ssize_t len;
822     int ret = 0;
823 
824     errno = 0;
825 
826     // Check if file is present
827     if (access(FRU_EEPROM, F_OK) == -1)
828     {
829         std::cerr << "Unable to access: " << FRU_EEPROM << std::endl;
830         return errno;
831     }
832 
833     // Open the file
834     fd = open(FRU_EEPROM, O_WRONLY);
835     if (fd == -1)
836     {
837         std::cerr << "Unable to open: " << FRU_EEPROM << std::endl;
838         return errno;
839     }
840 
841     // seek to the offset
842     lseek(fd, offset, SEEK_SET);
843 
844     // Write bytes to location
845     len = write(fd, guid, GUID_SIZE);
846     if (len != GUID_SIZE)
847     {
848         phosphor::logging::log<phosphor::logging::level::ERR>(
849             "GUID write data to EEPROM failed");
850         ret = errno;
851     }
852 
853     close(fd);
854     return ret;
855 }
856 
857 //----------------------------------------------------------------------
858 // Set System GUID (CMD_OEM_SET_SYSTEM_GUID)
859 //----------------------------------------------------------------------
860 ipmi_ret_t ipmiOemSetSystemGuid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
861                                 ipmi_request_t request,
862                                 ipmi_response_t response,
863                                 ipmi_data_len_t data_len,
864                                 ipmi_context_t context)
865 {
866     uint8_t* req = reinterpret_cast<uint8_t*>(request);
867 
868     if (*data_len != GUID_SIZE) // 16bytes
869     {
870         *data_len = 0;
871         return IPMI_CC_REQ_DATA_LEN_INVALID;
872     }
873 
874     *data_len = 0;
875 
876     if (setGUID(OFFSET_SYS_GUID, req))
877     {
878         return IPMI_CC_UNSPECIFIED_ERROR;
879     }
880     return IPMI_CC_OK;
881 }
882 
883 //----------------------------------------------------------------------
884 // Set Bios Flash Info (CMD_OEM_SET_BIOS_FLASH_INFO)
885 //----------------------------------------------------------------------
886 ipmi_ret_t ipmiOemSetBiosFlashInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
887                                    ipmi_request_t request,
888                                    ipmi_response_t response,
889                                    ipmi_data_len_t data_len,
890                                    ipmi_context_t context)
891 {
892     /* Do nothing, return success */
893     *data_len = 0;
894     return IPMI_CC_OK;
895 }
896 
897 //----------------------------------------------------------------------
898 // Set PPR (CMD_OEM_SET_PPR)
899 //----------------------------------------------------------------------
900 ipmi_ret_t ipmiOemSetPpr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
901                          ipmi_request_t request, ipmi_response_t response,
902                          ipmi_data_len_t data_len, ipmi_context_t context)
903 {
904     uint8_t* req = reinterpret_cast<uint8_t*>(request);
905     uint8_t pprCnt, pprAct, pprIndex;
906     uint8_t selParam = req[0];
907     uint8_t len = *data_len;
908     std::stringstream ss;
909     std::string str;
910 
911     *data_len = 0;
912 
913     switch (selParam)
914     {
915         case PPR_ACTION:
916             if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) ==
917                 oemData[KEY_PPR].end())
918                 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
919 
920             pprCnt = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
921             if (pprCnt == 0)
922                 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
923 
924             pprAct = req[1];
925             /* Check if ppr is enabled or disabled */
926             if (!(pprAct & 0x80))
927                 pprAct = 0;
928 
929             oemData[KEY_PPR][KEY_PPR_ACTION] = pprAct;
930             break;
931         case PPR_ROW_COUNT:
932             if (req[1] > 100)
933                 return IPMI_CC_PARM_OUT_OF_RANGE;
934 
935             oemData[KEY_PPR][KEY_PPR_ROW_COUNT] = req[1];
936             break;
937         case PPR_ROW_ADDR:
938             pprIndex = req[1];
939             if (pprIndex > 100)
940                 return IPMI_CC_PARM_OUT_OF_RANGE;
941 
942             if (len < PPR_ROW_ADDR_LEN + 1)
943             {
944                 phosphor::logging::log<phosphor::logging::level::ERR>(
945                     "Invalid PPR Row Address length received");
946                 return IPMI_CC_REQ_DATA_LEN_INVALID;
947             }
948 
949             ss << std::hex;
950             ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
951 
952             oemData[KEY_PPR][ss.str()][KEY_PPR_INDEX] = pprIndex;
953 
954             str = bytesToStr(&req[1], PPR_ROW_ADDR_LEN);
955             oemData[KEY_PPR][ss.str()][KEY_PPR_ROW_ADDR] = str.c_str();
956             break;
957         case PPR_HISTORY_DATA:
958             pprIndex = req[1];
959             if (pprIndex > 100)
960                 return IPMI_CC_PARM_OUT_OF_RANGE;
961 
962             if (len < PPR_HST_DATA_LEN + 1)
963             {
964                 phosphor::logging::log<phosphor::logging::level::ERR>(
965                     "Invalid PPR history data length received");
966                 return IPMI_CC_REQ_DATA_LEN_INVALID;
967             }
968 
969             ss << std::hex;
970             ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
971 
972             oemData[KEY_PPR][ss.str()][KEY_PPR_INDEX] = pprIndex;
973 
974             str = bytesToStr(&req[1], PPR_HST_DATA_LEN);
975             oemData[KEY_PPR][ss.str()][KEY_PPR_HST_DATA] = str.c_str();
976             break;
977         default:
978             return IPMI_CC_PARM_OUT_OF_RANGE;
979             break;
980     }
981 
982     flushOemData();
983 
984     return IPMI_CC_OK;
985 }
986 
987 //----------------------------------------------------------------------
988 // Get PPR (CMD_OEM_GET_PPR)
989 //----------------------------------------------------------------------
990 ipmi_ret_t ipmiOemGetPpr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
991                          ipmi_request_t request, ipmi_response_t response,
992                          ipmi_data_len_t data_len, ipmi_context_t context)
993 {
994     uint8_t* req = reinterpret_cast<uint8_t*>(request);
995     uint8_t* res = reinterpret_cast<uint8_t*>(response);
996     uint8_t pprCnt, pprIndex;
997     uint8_t selParam = req[0];
998     std::stringstream ss;
999     std::string str;
1000 
1001     /* Any failure will return zero length data */
1002     *data_len = 0;
1003 
1004     switch (selParam)
1005     {
1006         case PPR_ACTION:
1007             res[0] = 0;
1008             *data_len = 1;
1009 
1010             if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) !=
1011                 oemData[KEY_PPR].end())
1012             {
1013                 pprCnt = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
1014                 if (pprCnt != 0)
1015                 {
1016                     if (oemData[KEY_PPR].find(KEY_PPR_ACTION) !=
1017                         oemData[KEY_PPR].end())
1018                     {
1019                         res[0] = oemData[KEY_PPR][KEY_PPR_ACTION];
1020                     }
1021                 }
1022             }
1023             break;
1024         case PPR_ROW_COUNT:
1025             res[0] = 0;
1026             *data_len = 1;
1027             if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) !=
1028                 oemData[KEY_PPR].end())
1029                 res[0] = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
1030             break;
1031         case PPR_ROW_ADDR:
1032             pprIndex = req[1];
1033             if (pprIndex > 100)
1034                 return IPMI_CC_PARM_OUT_OF_RANGE;
1035 
1036             ss << std::hex;
1037             ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1038 
1039             if (oemData[KEY_PPR].find(ss.str()) == oemData[KEY_PPR].end())
1040                 return IPMI_CC_PARM_OUT_OF_RANGE;
1041 
1042             if (oemData[KEY_PPR][ss.str()].find(KEY_PPR_ROW_ADDR) ==
1043                 oemData[KEY_PPR][ss.str()].end())
1044                 return IPMI_CC_PARM_OUT_OF_RANGE;
1045 
1046             str = oemData[KEY_PPR][ss.str()][KEY_PPR_ROW_ADDR];
1047             *data_len = strToBytes(str, res);
1048             break;
1049         case PPR_HISTORY_DATA:
1050             pprIndex = req[1];
1051             if (pprIndex > 100)
1052                 return IPMI_CC_PARM_OUT_OF_RANGE;
1053 
1054             ss << std::hex;
1055             ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1056 
1057             if (oemData[KEY_PPR].find(ss.str()) == oemData[KEY_PPR].end())
1058                 return IPMI_CC_PARM_OUT_OF_RANGE;
1059 
1060             if (oemData[KEY_PPR][ss.str()].find(KEY_PPR_HST_DATA) ==
1061                 oemData[KEY_PPR][ss.str()].end())
1062                 return IPMI_CC_PARM_OUT_OF_RANGE;
1063 
1064             str = oemData[KEY_PPR][ss.str()][KEY_PPR_HST_DATA];
1065             *data_len = strToBytes(str, res);
1066             break;
1067         default:
1068             return IPMI_CC_PARM_OUT_OF_RANGE;
1069             break;
1070     }
1071 
1072     return IPMI_CC_OK;
1073 }
1074 
1075 /* FB OEM QC Commands */
1076 
1077 //----------------------------------------------------------------------
1078 // Set Proc Info (CMD_OEM_Q_SET_PROC_INFO)
1079 //----------------------------------------------------------------------
1080 //"Request:
1081 // Byte 1:3 – Manufacturer ID – XXYYZZ h, LSB first
1082 // Byte 4 – Processor Index, 0 base
1083 // Byte 5 – Parameter Selector
1084 // Byte 6..N – Configuration parameter data (see below for Parameters
1085 // of Processor Information)
1086 // Response:
1087 // Byte 1 – Completion code
1088 //
1089 // Parameter#1: (Processor Product Name)
1090 //
1091 // Byte 1..48 –Product name(ASCII code)
1092 // Ex. Intel(R) Xeon(R) CPU E5-2685 v3 @ 2.60GHz
1093 //
1094 // Param#2: Processor Basic Information
1095 // Byte 1 – Core Number
1096 // Byte 2 – Thread Number (LSB)
1097 // Byte 3 – Thread Number (MSB)
1098 // Byte 4 – Processor frequency in MHz (LSB)
1099 // Byte 5 – Processor frequency in MHz (MSB)
1100 // Byte 6..7 – Revision
1101 //
1102 ipmi_ret_t ipmiOemQSetProcInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1103                                ipmi_request_t request, ipmi_response_t response,
1104                                ipmi_data_len_t data_len, ipmi_context_t context)
1105 {
1106     qProcInfo_t* req = reinterpret_cast<qProcInfo_t*>(request);
1107     uint8_t numParam = sizeof(cpuInfoKey) / sizeof(uint8_t*);
1108     std::stringstream ss;
1109     std::string str;
1110     uint8_t len = *data_len;
1111 
1112     *data_len = 0;
1113 
1114     /* check for requested data params */
1115     if (len < 5 || req->paramSel < 1 || req->paramSel >= numParam)
1116     {
1117         phosphor::logging::log<phosphor::logging::level::ERR>(
1118             "Invalid parameter received");
1119         return IPMI_CC_PARM_OUT_OF_RANGE;
1120     }
1121 
1122     len = len - 5; // Get Actual data length
1123 
1124     ss << std::hex;
1125     ss << std::setw(2) << std::setfill('0') << (int)req->procIndex;
1126     oemData[KEY_Q_PROC_INFO][ss.str()][KEY_PROC_INDEX] = req->procIndex;
1127 
1128     str = bytesToStr(req->data, len);
1129     oemData[KEY_Q_PROC_INFO][ss.str()][cpuInfoKey[req->paramSel]] = str.c_str();
1130     flushOemData();
1131 
1132     return IPMI_CC_OK;
1133 }
1134 
1135 //----------------------------------------------------------------------
1136 // Get Proc Info (CMD_OEM_Q_GET_PROC_INFO)
1137 //----------------------------------------------------------------------
1138 // Request:
1139 // Byte 1:3 –  Manufacturer ID – XXYYZZ h, LSB first
1140 // Byte 4 – Processor Index, 0 base
1141 // Byte 5 – Parameter Selector
1142 // Response:
1143 // Byte 1 – Completion code
1144 // Byte 2..N – Configuration Parameter Data (see below for Parameters
1145 // of Processor Information)
1146 //
1147 // Parameter#1: (Processor Product Name)
1148 //
1149 // Byte 1..48 –Product name(ASCII code)
1150 // Ex. Intel(R) Xeon(R) CPU E5-2685 v3 @ 2.60GHz
1151 //
1152 // Param#2: Processor Basic Information
1153 // Byte 1 – Core Number
1154 // Byte 2 – Thread Number (LSB)
1155 // Byte 3 – Thread Number (MSB)
1156 // Byte 4 – Processor frequency in MHz (LSB)
1157 // Byte 5 – Processor frequency in MHz (MSB)
1158 // Byte 6..7 – Revision
1159 //
1160 ipmi_ret_t ipmiOemQGetProcInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1161                                ipmi_request_t request, ipmi_response_t response,
1162                                ipmi_data_len_t data_len, ipmi_context_t context)
1163 {
1164     qProcInfo_t* req = reinterpret_cast<qProcInfo_t*>(request);
1165     uint8_t numParam = sizeof(cpuInfoKey) / sizeof(uint8_t*);
1166     uint8_t* res = reinterpret_cast<uint8_t*>(response);
1167     std::stringstream ss;
1168     std::string str;
1169 
1170     *data_len = 0;
1171 
1172     /* check for requested data params */
1173     if (req->paramSel < 1 || req->paramSel >= numParam)
1174     {
1175         phosphor::logging::log<phosphor::logging::level::ERR>(
1176             "Invalid parameter received");
1177         return IPMI_CC_PARM_OUT_OF_RANGE;
1178     }
1179 
1180     ss << std::hex;
1181     ss << std::setw(2) << std::setfill('0') << (int)req->procIndex;
1182 
1183     if (oemData[KEY_Q_PROC_INFO].find(ss.str()) ==
1184         oemData[KEY_Q_PROC_INFO].end())
1185         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1186 
1187     if (oemData[KEY_Q_PROC_INFO][ss.str()].find(cpuInfoKey[req->paramSel]) ==
1188         oemData[KEY_Q_PROC_INFO][ss.str()].end())
1189         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1190 
1191     str = oemData[KEY_Q_PROC_INFO][ss.str()][cpuInfoKey[req->paramSel]];
1192     *data_len = strToBytes(str, res);
1193 
1194     return IPMI_CC_OK;
1195 }
1196 
1197 //----------------------------------------------------------------------
1198 // Set Dimm Info (CMD_OEM_Q_SET_DIMM_INFO)
1199 //----------------------------------------------------------------------
1200 // Request:
1201 // Byte 1:3 – Manufacturer ID – XXYYZZh, LSB first
1202 // Byte 4 – DIMM Index, 0 base
1203 // Byte 5 – Parameter Selector
1204 // Byte 6..N – Configuration parameter data (see below for Parameters
1205 // of DIMM Information)
1206 // Response:
1207 // Byte 1 – Completion code
1208 //
1209 // Param#1 (DIMM Location):
1210 // Byte 1 – DIMM Present
1211 // Byte 1 – DIMM Present
1212 // 01h – Present
1213 // FFh – Not Present
1214 // Byte 2 – Node Number, 0 base
1215 // Byte 3 – Channel Number , 0 base
1216 // Byte 4 – DIMM Number , 0 base
1217 //
1218 // Param#2 (DIMM Type):
1219 // Byte 1 – DIMM Type
1220 // Bit [7:6]
1221 // For DDR3
1222 //  00 – Normal Voltage (1.5V)
1223 //  01 – Ultra Low Voltage (1.25V)
1224 //  10 – Low Voltage (1.35V)
1225 //  11 – Reserved
1226 // For DDR4
1227 //  00 – Reserved
1228 //  01 – Reserved
1229 //  10 – Reserved
1230 //  11 – Normal Voltage (1.2V)
1231 // Bit [5:0]
1232 //  0x00 – SDRAM
1233 //  0x01 – DDR-1 RAM
1234 //  0x02 – Rambus
1235 //  0x03 – DDR-2 RAM
1236 //  0x04 – FBDIMM
1237 //  0x05 – DDR-3 RAM
1238 //  0x06 – DDR-4 RAM
1239 //
1240 // Param#3 (DIMM Speed):
1241 // Byte 1..2 – DIMM speed in MHz, LSB
1242 // Byte 3..6 – DIMM size in Mbytes, LSB
1243 //
1244 // Param#4 (Module Part Number):
1245 // Byte 1..20 –Module Part Number (JEDEC Standard No. 21-C)
1246 //
1247 // Param#5 (Module Serial Number):
1248 // Byte 1..4 –Module Serial Number (JEDEC Standard No. 21-C)
1249 //
1250 // Param#6 (Module Manufacturer ID):
1251 // Byte 1 - Module Manufacturer ID, LSB
1252 // Byte 2 - Module Manufacturer ID, MSB
1253 //
1254 ipmi_ret_t ipmiOemQSetDimmInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1255                                ipmi_request_t request, ipmi_response_t response,
1256                                ipmi_data_len_t data_len, ipmi_context_t context)
1257 {
1258     qDimmInfo_t* req = reinterpret_cast<qDimmInfo_t*>(request);
1259     uint8_t numParam = sizeof(dimmInfoKey) / sizeof(uint8_t*);
1260     std::stringstream ss;
1261     std::string str;
1262     uint8_t len = *data_len;
1263 
1264     *data_len = 0;
1265 
1266     /* check for requested data params */
1267     if (len < 5 || req->paramSel < 1 || req->paramSel >= numParam)
1268     {
1269         phosphor::logging::log<phosphor::logging::level::ERR>(
1270             "Invalid parameter received");
1271         return IPMI_CC_PARM_OUT_OF_RANGE;
1272     }
1273 
1274     len = len - 5; // Get Actual data length
1275 
1276     ss << std::hex;
1277     ss << std::setw(2) << std::setfill('0') << (int)req->dimmIndex;
1278     oemData[KEY_Q_DIMM_INFO][ss.str()][KEY_DIMM_INDEX] = req->dimmIndex;
1279 
1280     str = bytesToStr(req->data, len);
1281     oemData[KEY_Q_DIMM_INFO][ss.str()][dimmInfoKey[req->paramSel]] =
1282         str.c_str();
1283     flushOemData();
1284 
1285     return IPMI_CC_OK;
1286 }
1287 
1288 //----------------------------------------------------------------------
1289 // Get Dimm Info (CMD_OEM_Q_GET_DIMM_INFO)
1290 //----------------------------------------------------------------------
1291 // Request:
1292 // Byte 1:3 – Manufacturer ID – XXYYZZh, LSB first
1293 // Byte 4 – DIMM Index, 0 base
1294 // Byte 5 – Parameter Selector
1295 // Byte 6..N – Configuration parameter data (see below for Parameters
1296 // of DIMM Information)
1297 // Response:
1298 // Byte 1 – Completion code
1299 // Byte 2..N – Configuration Parameter Data (see Table_1213h Parameters
1300 // of DIMM Information)
1301 //
1302 // Param#1 (DIMM Location):
1303 // Byte 1 – DIMM Present
1304 // Byte 1 – DIMM Present
1305 // 01h – Present
1306 // FFh – Not Present
1307 // Byte 2 – Node Number, 0 base
1308 // Byte 3 – Channel Number , 0 base
1309 // Byte 4 – DIMM Number , 0 base
1310 //
1311 // Param#2 (DIMM Type):
1312 // Byte 1 – DIMM Type
1313 // Bit [7:6]
1314 // For DDR3
1315 //  00 – Normal Voltage (1.5V)
1316 //  01 – Ultra Low Voltage (1.25V)
1317 //  10 – Low Voltage (1.35V)
1318 //  11 – Reserved
1319 // For DDR4
1320 //  00 – Reserved
1321 //  01 – Reserved
1322 //  10 – Reserved
1323 //  11 – Normal Voltage (1.2V)
1324 // Bit [5:0]
1325 //  0x00 – SDRAM
1326 //  0x01 – DDR-1 RAM
1327 //  0x02 – Rambus
1328 //  0x03 – DDR-2 RAM
1329 //  0x04 – FBDIMM
1330 //  0x05 – DDR-3 RAM
1331 //  0x06 – DDR-4 RAM
1332 //
1333 // Param#3 (DIMM Speed):
1334 // Byte 1..2 – DIMM speed in MHz, LSB
1335 // Byte 3..6 – DIMM size in Mbytes, LSB
1336 //
1337 // Param#4 (Module Part Number):
1338 // Byte 1..20 –Module Part Number (JEDEC Standard No. 21-C)
1339 //
1340 // Param#5 (Module Serial Number):
1341 // Byte 1..4 –Module Serial Number (JEDEC Standard No. 21-C)
1342 //
1343 // Param#6 (Module Manufacturer ID):
1344 // Byte 1 - Module Manufacturer ID, LSB
1345 // Byte 2 - Module Manufacturer ID, MSB
1346 //
1347 ipmi_ret_t ipmiOemQGetDimmInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1348                                ipmi_request_t request, ipmi_response_t response,
1349                                ipmi_data_len_t data_len, ipmi_context_t context)
1350 {
1351     qDimmInfo_t* req = reinterpret_cast<qDimmInfo_t*>(request);
1352     uint8_t numParam = sizeof(dimmInfoKey) / sizeof(uint8_t*);
1353     uint8_t* res = reinterpret_cast<uint8_t*>(response);
1354     std::stringstream ss;
1355     std::string str;
1356 
1357     *data_len = 0;
1358 
1359     /* check for requested data params */
1360     if (req->paramSel < 1 || req->paramSel >= numParam)
1361     {
1362         phosphor::logging::log<phosphor::logging::level::ERR>(
1363             "Invalid parameter received");
1364         return IPMI_CC_PARM_OUT_OF_RANGE;
1365     }
1366 
1367     ss << std::hex;
1368     ss << std::setw(2) << std::setfill('0') << (int)req->dimmIndex;
1369 
1370     if (oemData[KEY_Q_DIMM_INFO].find(ss.str()) ==
1371         oemData[KEY_Q_DIMM_INFO].end())
1372         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1373 
1374     if (oemData[KEY_Q_DIMM_INFO][ss.str()].find(dimmInfoKey[req->paramSel]) ==
1375         oemData[KEY_Q_DIMM_INFO][ss.str()].end())
1376         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1377 
1378     str = oemData[KEY_Q_DIMM_INFO][ss.str()][dimmInfoKey[req->paramSel]];
1379     *data_len = strToBytes(str, res);
1380 
1381     return IPMI_CC_OK;
1382 }
1383 
1384 //----------------------------------------------------------------------
1385 // Set Drive Info (CMD_OEM_Q_SET_DRIVE_INFO)
1386 //----------------------------------------------------------------------
1387 // BIOS issue this command to provide HDD information to BMC.
1388 //
1389 // BIOS just can get information by standard ATA / SMART command for
1390 // OB SATA controller.
1391 // BIOS can get
1392 // 1.     Serial Number
1393 // 2.     Model Name
1394 // 3.     HDD FW Version
1395 // 4.     HDD Capacity
1396 // 5.     HDD WWN
1397 //
1398 //  Use Get HDD info Param #5 to know the MAX HDD info index.
1399 //
1400 //  Request:
1401 //  Byte 1:3 – Quanta Manufacturer ID – 001C4Ch, LSB first
1402 //  Byte 4 –
1403 //  [7:4] Reserved
1404 //  [3:0] HDD Controller Type
1405 //     0x00 – BIOS
1406 //     0x01 – Expander
1407 //     0x02 – LSI
1408 //  Byte 5 – HDD Info Index, 0 base
1409 //  Byte 6 – Parameter Selector
1410 //  Byte 7..N – Configuration parameter data (see Table_1415h Parameters of HDD
1411 //  Information)
1412 //
1413 //  Response:
1414 //  Byte 1 – Completion Code
1415 //
1416 //  Param#0 (HDD Location):
1417 //  Byte 1 – Controller
1418 //    [7:3] Device Number
1419 //    [2:0] Function Number
1420 //  For Intel C610 series (Wellsburg)
1421 //    D31:F2 (0xFA) – SATA control 1
1422 //    D31:F5 (0xFD) – SATA control 2
1423 //    D17:F4 (0x8C) – sSata control
1424 //  Byte 2 – Port Number
1425 //  Byte 3 – Location (0xFF: No HDD Present)
1426 //  BIOS default set Byte 3 to 0xFF, if No HDD Present. And then skip send param
1427 //  #1~4, #6,  #7 to BMC (still send param #5) BIOS default set Byte 3 to 0, if
1428 //  the HDD present. BMC or other people who know the HDD location has
1429 //  responsibility for update Location info
1430 //
1431 //  Param#1 (Serial Number):
1432 //  Bytes 1..33: HDD Serial Number
1433 //
1434 //  Param#2 (Model Name):
1435 //  Byte 1..33 – HDD Model Name
1436 //
1437 //  Param#3 (HDD FW Version):
1438 //  Byte 1..17 –HDD FW version
1439 //
1440 //  Param#4 (Capacity):
1441 //  Byte 1..4 –HDD Block Size, LSB
1442 //  Byte 5..12 - HDD Block Number, LSB
1443 //  HDD Capacity = HDD Block size * HDD BLock number  (Unit Byte)
1444 //
1445 //  Param#5 (Max HDD Quantity):
1446 //  Byte 1 - Max HDD Quantity
1447 //  Max supported port numbers in this PCH
1448 //
1449 //  Param#6 (HDD Type)
1450 //  Byte 1 – HDD Type
1451 //  0h – Reserved
1452 //  1h – SAS
1453 //  2h – SATA
1454 //  3h – PCIE SSD (NVME)
1455 //
1456 //  Param#7 (HDD WWN)
1457 //  Data 1...8: HDD World Wide Name, LSB
1458 //
1459 ipmi_ret_t ipmiOemQSetDriveInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1460                                 ipmi_request_t request,
1461                                 ipmi_response_t response,
1462                                 ipmi_data_len_t data_len,
1463                                 ipmi_context_t context)
1464 {
1465     qDriveInfo_t* req = reinterpret_cast<qDriveInfo_t*>(request);
1466     uint8_t numParam = sizeof(driveInfoKey) / sizeof(uint8_t*);
1467     uint8_t ctrlType = req->hddCtrlType & 0x0f;
1468     std::stringstream ss;
1469     std::string str;
1470     uint8_t len = *data_len;
1471 
1472     *data_len = 0;
1473 
1474     /* check for requested data params */
1475     if (len < 6 || req->paramSel < 1 || req->paramSel >= numParam ||
1476         ctrlType > 2)
1477     {
1478         phosphor::logging::log<phosphor::logging::level::ERR>(
1479             "Invalid parameter received");
1480         return IPMI_CC_PARM_OUT_OF_RANGE;
1481     }
1482 
1483     len = len - 6; // Get Actual data length
1484 
1485     ss << std::hex;
1486     ss << std::setw(2) << std::setfill('0') << (int)req->hddIndex;
1487     oemData[KEY_Q_DRIVE_INFO][KEY_HDD_CTRL_TYPE] = req->hddCtrlType;
1488     oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()][KEY_HDD_INDEX] =
1489         req->hddIndex;
1490 
1491     str = bytesToStr(req->data, len);
1492     oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()]
1493            [driveInfoKey[req->paramSel]] = str.c_str();
1494     flushOemData();
1495 
1496     return IPMI_CC_OK;
1497 }
1498 
1499 //----------------------------------------------------------------------
1500 // Get Drive Info (CMD_OEM_Q_GET_DRIVE_INFO)
1501 //----------------------------------------------------------------------
1502 // BMC needs to check HDD presented or not first. If NOT presented, return
1503 // completion code 0xD5.
1504 //
1505 // Request:
1506 // Byte 1:3 – Quanta Manufacturer ID – 001C4Ch, LSB first
1507 // Byte 4 –
1508 //[7:4] Reserved
1509 //[3:0] HDD Controller Type
1510 //   0x00 – BIOS
1511 //   0x01 – Expander
1512 //   0x02 – LSI
1513 // Byte 5 – HDD Index, 0 base
1514 // Byte 6 – Parameter Selector (See Above Set HDD Information)
1515 // Response:
1516 // Byte 1 – Completion Code
1517 //   0xD5 – Not support in current status (HDD Not Present)
1518 // Byte 2..N – Configuration parameter data (see Table_1415h Parameters of HDD
1519 // Information)
1520 //
1521 ipmi_ret_t ipmiOemQGetDriveInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1522                                 ipmi_request_t request,
1523                                 ipmi_response_t response,
1524                                 ipmi_data_len_t data_len,
1525                                 ipmi_context_t context)
1526 {
1527     qDriveInfo_t* req = reinterpret_cast<qDriveInfo_t*>(request);
1528     uint8_t numParam = sizeof(driveInfoKey) / sizeof(uint8_t*);
1529     uint8_t* res = reinterpret_cast<uint8_t*>(response);
1530     uint8_t ctrlType = req->hddCtrlType & 0x0f;
1531     std::stringstream ss;
1532     std::string str;
1533 
1534     *data_len = 0;
1535 
1536     /* check for requested data params */
1537     if (req->paramSel < 1 || req->paramSel >= numParam || ctrlType > 2)
1538     {
1539         phosphor::logging::log<phosphor::logging::level::ERR>(
1540             "Invalid parameter received");
1541         return IPMI_CC_PARM_OUT_OF_RANGE;
1542     }
1543 
1544     if (oemData[KEY_Q_DRIVE_INFO].find(ctrlTypeKey[ctrlType]) ==
1545         oemData[KEY_Q_DRIVE_INFO].end())
1546         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1547 
1548     ss << std::hex;
1549     ss << std::setw(2) << std::setfill('0') << (int)req->hddIndex;
1550 
1551     if (oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]].find(ss.str()) ==
1552         oemData[KEY_Q_DRIVE_INFO].end())
1553         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1554 
1555     if (oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()].find(
1556             dimmInfoKey[req->paramSel]) ==
1557         oemData[KEY_Q_DRIVE_INFO][ss.str()].end())
1558         return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1559 
1560     str = oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()]
1561                  [dimmInfoKey[req->paramSel]];
1562     *data_len = strToBytes(str, res);
1563 
1564     return IPMI_CC_OK;
1565 }
1566 
1567 /* Helper function for sending DCMI commands to ME and getting response back */
1568 ipmi::RspType<std::vector<uint8_t>> sendDCMICmd(uint8_t cmd,
1569                                                 std::vector<uint8_t>& cmdData)
1570 {
1571     std::vector<uint8_t> respData;
1572 
1573     /* Add group id as first byte to request for ME command */
1574     cmdData.insert(cmdData.begin(), groupDCMI);
1575 
1576     if (sendMeCmd(ipmi::netFnGroup, cmd, cmdData, respData))
1577         return ipmi::responseUnspecifiedError();
1578 
1579     /* Remove group id as first byte as it will be added by IPMID */
1580     respData.erase(respData.begin());
1581 
1582     return ipmi::responseSuccess(std::move(respData));
1583 }
1584 
1585 /* DCMI Command handellers. */
1586 
1587 ipmi::RspType<std::vector<uint8_t>>
1588     ipmiOemDCMIGetPowerReading(std::vector<uint8_t> reqData)
1589 {
1590     return sendDCMICmd(ipmi::dcmi::cmdGetPowerReading, reqData);
1591 }
1592 
1593 ipmi::RspType<std::vector<uint8_t>>
1594     ipmiOemDCMIGetPowerLimit(std::vector<uint8_t> reqData)
1595 {
1596     return sendDCMICmd(ipmi::dcmi::cmdGetPowerLimit, reqData);
1597 }
1598 
1599 ipmi::RspType<std::vector<uint8_t>>
1600     ipmiOemDCMISetPowerLimit(std::vector<uint8_t> reqData)
1601 {
1602     return sendDCMICmd(ipmi::dcmi::cmdSetPowerLimit, reqData);
1603 }
1604 
1605 ipmi::RspType<std::vector<uint8_t>>
1606     ipmiOemDCMIApplyPowerLimit(std::vector<uint8_t> reqData)
1607 {
1608     return sendDCMICmd(ipmi::dcmi::cmdActDeactivatePwrLimit, reqData);
1609 }
1610 
1611 static void registerOEMFunctions(void)
1612 {
1613     /* Get OEM data from json file */
1614     std::ifstream file(JSON_OEM_DATA_FILE);
1615     if (file)
1616     {
1617         file >> oemData;
1618         file.close();
1619     }
1620 
1621     phosphor::logging::log<phosphor::logging::level::INFO>(
1622         "Registering OEM commands");
1623 
1624     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_INFO,
1625                          NULL, ipmiOemDbgGetFrameInfo,
1626                          PRIVILEGE_USER); // get debug frame info
1627     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ,
1628                          CMD_OEM_USB_DBG_GET_UPDATED_FRAMES, NULL,
1629                          ipmiOemDbgGetUpdFrames,
1630                          PRIVILEGE_USER); // get debug updated frames
1631     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_POST_DESC,
1632                          NULL, ipmiOemDbgGetPostDesc,
1633                          PRIVILEGE_USER); // get debug post description
1634     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_GPIO_DESC,
1635                          NULL, ipmiOemDbgGetGpioDesc,
1636                          PRIVILEGE_USER); // get debug gpio description
1637     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_DATA,
1638                          NULL, ipmiOemDbgGetFrameData,
1639                          PRIVILEGE_USER); // get debug frame data
1640     ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_CTRL_PANEL,
1641                          NULL, ipmiOemDbgGetCtrlPanel,
1642                          PRIVILEGE_USER); // get debug control panel
1643     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_DIMM_INFO, NULL,
1644                          ipmiOemSetDimmInfo,
1645                          PRIVILEGE_USER); // Set Dimm Info
1646     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_BOARD_ID, NULL,
1647                          ipmiOemGetBoardID,
1648                          PRIVILEGE_USER); // Get Board ID
1649     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_BOOT_ORDER, NULL,
1650                          ipmiOemSetBootOrder,
1651                          PRIVILEGE_USER); // Set Boot Order
1652     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_BOOT_ORDER, NULL,
1653                          ipmiOemGetBootOrder,
1654                          PRIVILEGE_USER); // Get Boot Order
1655     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_MACHINE_CONFIG_INFO, NULL,
1656                          ipmiOemSetMachineCfgInfo,
1657                          PRIVILEGE_USER); // Set Machine Config Info
1658     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_POST_START, NULL,
1659                          ipmiOemSetPostStart,
1660                          PRIVILEGE_USER); // Set POST start
1661     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_POST_END, NULL,
1662                          ipmiOemSetPostEnd,
1663                          PRIVILEGE_USER); // Set POST End
1664     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_PPIN_INFO, NULL,
1665                          ipmiOemSetPPINInfo,
1666                          PRIVILEGE_USER); // Set PPIN Info
1667     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_SYSTEM_GUID, NULL,
1668                          ipmiOemSetSystemGuid,
1669                          PRIVILEGE_USER); // Set System GUID
1670     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_ADR_TRIGGER, NULL,
1671                          ipmiOemSetAdrTrigger,
1672                          PRIVILEGE_USER); // Set ADR Trigger
1673     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_BIOS_FLASH_INFO, NULL,
1674                          ipmiOemSetBiosFlashInfo,
1675                          PRIVILEGE_USER); // Set Bios Flash Info
1676     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_PPR, NULL, ipmiOemSetPpr,
1677                          PRIVILEGE_USER); // Set PPR
1678     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_PPR, NULL, ipmiOemGetPpr,
1679                          PRIVILEGE_USER); // Get PPR
1680     /* FB OEM QC Commands */
1681     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_SET_PROC_INFO, NULL,
1682                          ipmiOemQSetProcInfo,
1683                          PRIVILEGE_USER); // Set Proc Info
1684     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_GET_PROC_INFO, NULL,
1685                          ipmiOemQGetProcInfo,
1686                          PRIVILEGE_USER); // Get Proc Info
1687     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_SET_DIMM_INFO, NULL,
1688                          ipmiOemQSetDimmInfo,
1689                          PRIVILEGE_USER); // Set Dimm Info
1690     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_GET_DIMM_INFO, NULL,
1691                          ipmiOemQGetDimmInfo,
1692                          PRIVILEGE_USER); // Get Dimm Info
1693     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_SET_DRIVE_INFO, NULL,
1694                          ipmiOemQSetDriveInfo,
1695                          PRIVILEGE_USER); // Set Drive Info
1696     ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_GET_DRIVE_INFO, NULL,
1697                          ipmiOemQGetDriveInfo,
1698                          PRIVILEGE_USER); // Get Drive Info
1699 
1700     /* FB OEM DCMI Commands as per DCMI spec 1.5 Section 6 */
1701     ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, groupDCMI,
1702                                ipmi::dcmi::cmdGetPowerReading,
1703                                ipmi::Privilege::User,
1704                                ipmiOemDCMIGetPowerReading); // Get Power Reading
1705 
1706     ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, groupDCMI,
1707                                ipmi::dcmi::cmdGetPowerLimit,
1708                                ipmi::Privilege::User,
1709                                ipmiOemDCMIGetPowerLimit); // Get Power Limit
1710 
1711     ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, groupDCMI,
1712                                ipmi::dcmi::cmdSetPowerLimit,
1713                                ipmi::Privilege::Operator,
1714                                ipmiOemDCMISetPowerLimit); // Set Power Limit
1715 
1716     ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, groupDCMI,
1717                                ipmi::dcmi::cmdActDeactivatePwrLimit,
1718                                ipmi::Privilege::Operator,
1719                                ipmiOemDCMIApplyPowerLimit); // Apply Power Limit
1720 
1721     return;
1722 }
1723 
1724 } // namespace ipmi
1725