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