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