1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "xyz/openbmc_project/Common/error.hpp"
19
20 #include <boost/crc.hpp>
21 #include <commandutils.hpp>
22 #include <ipmid/api-types.hpp>
23 #include <ipmid/api.hpp>
24 #include <ipmid/utils.hpp>
25 #include <nlohmann/json.hpp>
26 #include <oemcommands.hpp>
27 #include <phosphor-logging/log.hpp>
28 #include <sdbusplus/bus.hpp>
29 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
30 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
31 #include <xyz/openbmc_project/Control/Boot/Type/server.hpp>
32
33 #include <array>
34 #include <cstring>
35 #include <fstream>
36 #include <functional>
37 #include <iomanip>
38 #include <iostream>
39 #include <optional>
40 #include <regex>
41 #include <sstream>
42 #include <string>
43 #include <vector>
44
45 #define SIZE_IANA_ID 3
46
47 namespace ipmi
48 {
49
50 using namespace phosphor::logging;
51
52 void getSelectorPosition(size_t& position);
53 static void registerOEMFunctions() __attribute__((constructor));
54 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
55 static constexpr size_t maxFRUStringLength = 0x3F;
56 constexpr uint8_t cmdSetSystemGuid = 0xEF;
57
58 constexpr uint8_t cmdSetQDimmInfo = 0x12;
59 constexpr uint8_t cmdGetQDimmInfo = 0x13;
60
61 constexpr ipmi_ret_t ccInvalidParam = 0x80;
62
63 int plat_udbg_get_post_desc(uint8_t, uint8_t*, uint8_t, uint8_t*, uint8_t*,
64 uint8_t*);
65 int plat_udbg_get_gpio_desc(uint8_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*,
66 uint8_t*);
67 int plat_udbg_get_frame_data(uint8_t, uint8_t, uint8_t*, uint8_t*, uint8_t*);
68 ipmi_ret_t plat_udbg_control_panel(uint8_t, uint8_t, uint8_t, uint8_t*,
69 uint8_t*);
70 int sendMeCmd(uint8_t, uint8_t, std::vector<uint8_t>&, std::vector<uint8_t>&);
71
72 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
73 std::vector<uint8_t>&);
74
75 nlohmann::json oemData __attribute__((init_priority(101)));
76
77 constexpr const char* certPath = "/mnt/data/host/bios-rootcert";
78
79 static constexpr size_t GUID_SIZE = 16;
80 // TODO Make offset and location runtime configurable to ensure we
81 // can make each define their own locations.
82 static constexpr off_t OFFSET_SYS_GUID = 0x17F0;
83 static constexpr const char* FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom";
84 void flushOemData();
85
86 enum class LanParam : uint8_t
87 {
88 INPROGRESS = 0,
89 AUTHSUPPORT = 1,
90 AUTHENABLES = 2,
91 IP = 3,
92 IPSRC = 4,
93 MAC = 5,
94 SUBNET = 6,
95 GATEWAY = 12,
96 VLAN = 20,
97 CIPHER_SUITE_COUNT = 22,
98 CIPHER_SUITE_ENTRIES = 23,
99 IPV6 = 59,
100 };
101
102 namespace network
103 {
104
105 constexpr auto ROOT = "/xyz/openbmc_project/network";
106 constexpr auto SERVICE = "xyz.openbmc_project.Network";
107 constexpr auto IPV4_TYPE = "ipv4";
108 constexpr auto IPV6_TYPE = "ipv6";
109 constexpr auto IPV4_PREFIX = "169.254";
110 constexpr auto IPV6_PREFIX = "fe80";
111 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
112 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
113 constexpr auto IPV4_PROTOCOL = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
114 constexpr auto IPV6_PROTOCOL = "xyz.openbmc_project.Network.IP.Protocol.IPv6";
115
isLinkLocalIP(const std::string & address)116 bool isLinkLocalIP(const std::string& address)
117 {
118 return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
119 }
120
getIPObject(sdbusplus::bus_t & bus,const std::string & interface,const std::string & serviceRoot,const std::string & protocol,const std::string & ethdev)121 DbusObjectInfo getIPObject(sdbusplus::bus_t& bus, const std::string& interface,
122 const std::string& serviceRoot,
123 const std::string& protocol,
124 const std::string& ethdev)
125 {
126 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, ethdev);
127
128 if (objectTree.empty())
129 {
130 log<level::ERR>("No Object has implemented the IP interface",
131 entry("INTERFACE=%s", interface.c_str()));
132 }
133
134 DbusObjectInfo objectInfo;
135
136 for (auto& object : objectTree)
137 {
138 auto variant =
139 ipmi::getDbusProperty(bus, object.second.begin()->first,
140 object.first, IP_INTERFACE, "Type");
141 if (std::get<std::string>(variant) != protocol)
142 {
143 continue;
144 }
145
146 variant = ipmi::getDbusProperty(bus, object.second.begin()->first,
147 object.first, IP_INTERFACE, "Address");
148
149 objectInfo = std::make_pair(object.first, object.second.begin()->first);
150
151 // if LinkLocalIP found look for Non-LinkLocalIP
152 if (isLinkLocalIP(std::get<std::string>(variant)))
153 {
154 continue;
155 }
156 else
157 {
158 break;
159 }
160 }
161 return objectInfo;
162 }
163
164 } // namespace network
165
166 namespace boot
167 {
168 using BootSource =
169 sdbusplus::xyz::openbmc_project::Control::Boot::server::Source::Sources;
170 using BootMode =
171 sdbusplus::xyz::openbmc_project::Control::Boot::server::Mode::Modes;
172 using BootType =
173 sdbusplus::xyz::openbmc_project::Control::Boot::server::Type::Types;
174
175 using IpmiValue = uint8_t;
176
177 std::map<IpmiValue, BootSource> sourceIpmiToDbus = {
178 {0x0f, BootSource::Default}, {0x00, BootSource::RemovableMedia},
179 {0x01, BootSource::Network}, {0x02, BootSource::Disk},
180 {0x03, BootSource::ExternalMedia}, {0x04, BootSource::RemovableMedia},
181 {0x09, BootSource::Network}};
182
183 std::map<IpmiValue, BootMode> modeIpmiToDbus = {{0x04, BootMode::Setup},
184 {0x00, BootMode::Regular}};
185
186 std::map<IpmiValue, BootType> typeIpmiToDbus = {{0x00, BootType::Legacy},
187 {0x01, BootType::EFI}};
188
189 std::map<std::optional<BootSource>, IpmiValue> sourceDbusToIpmi = {
190 {BootSource::Default, 0x0f},
191 {BootSource::RemovableMedia, 0x00},
192 {BootSource::Network, 0x01},
193 {BootSource::Disk, 0x02},
194 {BootSource::ExternalMedia, 0x03}};
195
196 std::map<std::optional<BootMode>, IpmiValue> modeDbusToIpmi = {
197 {BootMode::Setup, 0x04}, {BootMode::Regular, 0x00}};
198
199 std::map<std::optional<BootType>, IpmiValue> typeDbusToIpmi = {
200 {BootType::Legacy, 0x00}, {BootType::EFI, 0x01}};
201
202 static constexpr auto bootEnableIntf = "xyz.openbmc_project.Object.Enable";
203 static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
204 static constexpr auto bootSourceIntf =
205 "xyz.openbmc_project.Control.Boot.Source";
206 static constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type";
207 static constexpr auto bootSourceProp = "BootSource";
208 static constexpr auto bootModeProp = "BootMode";
209 static constexpr auto bootTypeProp = "BootType";
210 static constexpr auto bootEnableProp = "Enabled";
211
objPath(size_t id)212 std::tuple<std::string, std::string> objPath(size_t id)
213 {
214 std::string hostName = "host" + std::to_string(id);
215 std::string bootObjPath =
216 "/xyz/openbmc_project/control/" + hostName + "/boot";
217 return std::make_tuple(std::move(bootObjPath), std::move(hostName));
218 }
219
220 /* Helper functions to set boot order */
setBootOrder(std::string bootObjPath,const std::vector<uint8_t> & bootSeq,std::string bootOrderKey)221 void setBootOrder(std::string bootObjPath, const std::vector<uint8_t>& bootSeq,
222 std::string bootOrderKey)
223 {
224 if (bootSeq.size() != SIZE_BOOT_ORDER)
225 {
226 phosphor::logging::log<phosphor::logging::level::ERR>(
227 "Invalid Boot order length received");
228 return;
229 }
230
231 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
232
233 uint8_t mode = bootSeq.front();
234
235 // SETTING BOOT MODE PROPERTY
236 uint8_t bootModeBit = mode & 0x04;
237 auto bootValue = ipmi::boot::modeIpmiToDbus.at(bootModeBit);
238
239 std::string bootOption =
240 sdbusplus::message::convert_to_string<boot::BootMode>(bootValue);
241
242 std::string service =
243 getService(*dbus, ipmi::boot::bootModeIntf, bootObjPath);
244 setDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootModeIntf,
245 ipmi::boot::bootModeProp, bootOption);
246
247 // SETTING BOOT SOURCE PROPERTY
248 auto bootOrder = ipmi::boot::sourceIpmiToDbus.at(bootSeq.at(1));
249 std::string bootSource =
250 sdbusplus::message::convert_to_string<boot::BootSource>(bootOrder);
251
252 service = getService(*dbus, ipmi::boot::bootSourceIntf, bootObjPath);
253 setDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootSourceIntf,
254 ipmi::boot::bootSourceProp, bootSource);
255
256 // SETTING BOOT TYPE PROPERTY
257 uint8_t bootTypeBit = mode & 0x01;
258 auto bootTypeVal = ipmi::boot::typeIpmiToDbus.at(bootTypeBit);
259
260 std::string bootType =
261 sdbusplus::message::convert_to_string<boot::BootType>(bootTypeVal);
262
263 service = getService(*dbus, ipmi::boot::bootTypeIntf, bootObjPath);
264
265 setDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootTypeIntf,
266 ipmi::boot::bootTypeProp, bootType);
267
268 // Set the valid bit to boot enabled property
269 service = getService(*dbus, ipmi::boot::bootEnableIntf, bootObjPath);
270
271 setDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootEnableIntf,
272 ipmi::boot::bootEnableProp,
273 (mode & BOOT_MODE_BOOT_FLAG) ? true : false);
274
275 nlohmann::json bootMode;
276
277 bootMode["UEFI"] = (mode & BOOT_MODE_UEFI) ? true : false;
278 bootMode["CMOS_CLR"] = (mode & BOOT_MODE_CMOS_CLR) ? true : false;
279 bootMode["FORCE_BOOT"] = (mode & BOOT_MODE_FORCE_BOOT) ? true : false;
280 bootMode["BOOT_FLAG"] = (mode & BOOT_MODE_BOOT_FLAG) ? true : false;
281 oemData[bootOrderKey][KEY_BOOT_MODE] = bootMode;
282
283 /* Initialize boot sequence array */
284 oemData[bootOrderKey][KEY_BOOT_SEQ] = {};
285 for (size_t i = 1; i < SIZE_BOOT_ORDER; i++)
286 {
287 if (bootSeq.at(i) >= BOOT_SEQ_ARRAY_SIZE)
288 oemData[bootOrderKey][KEY_BOOT_SEQ][i - 1] = "NA";
289 else
290 oemData[bootOrderKey][KEY_BOOT_SEQ][i - 1] =
291 bootSeqDefine[bootSeq.at(i)];
292 }
293
294 flushOemData();
295 }
296
getBootOrder(std::string bootObjPath,std::vector<uint8_t> & bootSeq,std::string hostName)297 void getBootOrder(std::string bootObjPath, std::vector<uint8_t>& bootSeq,
298 std::string hostName)
299 {
300 if (oemData.find(hostName) == oemData.end())
301 {
302 /* Return default boot order 0100090203ff */
303 bootSeq.push_back(BOOT_MODE_UEFI);
304 bootSeq.push_back(static_cast<uint8_t>(bootMap["USB_DEV"]));
305 bootSeq.push_back(static_cast<uint8_t>(bootMap["NET_IPV6"]));
306 bootSeq.push_back(static_cast<uint8_t>(bootMap["SATA_HDD"]));
307 bootSeq.push_back(static_cast<uint8_t>(bootMap["SATA_CD"]));
308 bootSeq.push_back(0xff);
309
310 phosphor::logging::log<phosphor::logging::level::INFO>(
311 "Set default boot order");
312 setBootOrder(bootObjPath, bootSeq, hostName);
313 return;
314 }
315
316 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
317
318 // GETTING PROPERTY OF MODE INTERFACE
319
320 std::string service =
321 getService(*dbus, ipmi::boot::bootModeIntf, bootObjPath);
322 Value variant =
323 getDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootModeIntf,
324 ipmi::boot::bootModeProp);
325
326 auto bootMode = sdbusplus::message::convert_from_string<boot::BootMode>(
327 std::get<std::string>(variant));
328
329 uint8_t bootOption = ipmi::boot::modeDbusToIpmi.at(bootMode);
330
331 // GETTING PROPERTY OF TYPE INTERFACE
332
333 service = getService(*dbus, ipmi::boot::bootTypeIntf, bootObjPath);
334 variant =
335 getDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootTypeIntf,
336 ipmi::boot::bootTypeProp);
337
338 auto bootType = sdbusplus::message::convert_from_string<boot::BootType>(
339 std::get<std::string>(variant));
340
341 // Get the valid bit to boot enabled property
342 service = getService(*dbus, ipmi::boot::bootEnableIntf, bootObjPath);
343 variant =
344 getDbusProperty(*dbus, service, bootObjPath, ipmi::boot::bootEnableIntf,
345 ipmi::boot::bootEnableProp);
346
347 bool validFlag = std::get<bool>(variant);
348
349 uint8_t bootTypeVal = ipmi::boot::typeDbusToIpmi.at(bootType);
350
351 bootSeq.push_back(bootOption | bootTypeVal);
352
353 if (validFlag)
354 {
355 bootSeq.front() |= BOOT_MODE_BOOT_FLAG;
356 }
357
358 nlohmann::json bootModeJson = oemData[hostName][KEY_BOOT_MODE];
359 if (bootModeJson["CMOS_CLR"])
360 bootSeq.front() |= BOOT_MODE_CMOS_CLR;
361
362 for (int i = 1; i < SIZE_BOOT_ORDER; i++)
363 {
364 std::string seqStr = oemData[hostName][KEY_BOOT_SEQ][i - 1];
365 if (bootMap.find(seqStr) != bootMap.end())
366 bootSeq.push_back(bootMap[seqStr]);
367 else
368 bootSeq.push_back(0xff);
369 }
370 }
371
372 } // namespace boot
373
374 //----------------------------------------------------------------------
375 // Helper functions for storing oem data
376 //----------------------------------------------------------------------
377
flushOemData()378 void flushOemData()
379 {
380 std::ofstream file(JSON_OEM_DATA_FILE);
381 file << oemData;
382 file.close();
383 return;
384 }
385
bytesToStr(uint8_t * byte,int len)386 std::string bytesToStr(uint8_t* byte, int len)
387 {
388 std::stringstream ss;
389 int i;
390
391 ss << std::hex;
392 for (i = 0; i < len; i++)
393 {
394 ss << std::setw(2) << std::setfill('0') << (int)byte[i];
395 }
396
397 return ss.str();
398 }
399
strToBytes(std::string & str,uint8_t * data)400 int strToBytes(std::string& str, uint8_t* data)
401 {
402 std::string sstr;
403 size_t i;
404
405 for (i = 0; i < (str.length()) / 2; i++)
406 {
407 sstr = str.substr(i * 2, 2);
408 data[i] = (uint8_t)std::strtol(sstr.c_str(), NULL, 16);
409 }
410 return i;
411 }
412
readDimmType(std::string & data,uint8_t param)413 int readDimmType(std::string& data, uint8_t param)
414 {
415 nlohmann::json dimmObj;
416 /* Get dimm type names stored in json file */
417 std::ifstream file(JSON_DIMM_TYPE_FILE);
418 if (file)
419 {
420 file >> dimmObj;
421 file.close();
422 }
423 else
424 {
425 phosphor::logging::log<phosphor::logging::level::ERR>(
426 "DIMM type names file not found",
427 phosphor::logging::entry("DIMM_TYPE_FILE=%s", JSON_DIMM_TYPE_FILE));
428 return -1;
429 }
430
431 std::string dimmKey = "dimm_type" + std::to_string(param);
432 auto obj = dimmObj[dimmKey]["short_name"];
433 data = obj;
434 return 0;
435 }
436
findIpAddress(sdbusplus::bus_t & bus,const std::string & protocol,const std::vector<std::string> & ifaces)437 static std::optional<std::string> findIpAddress(
438 sdbusplus::bus_t& bus, const std::string& protocol,
439 const std::vector<std::string>& ifaces)
440 {
441 for (auto& dev : ifaces)
442 {
443 try
444 {
445 auto ipObjectInfo =
446 ipmi::network::getIPObject(bus, ipmi::network::IP_INTERFACE,
447 ipmi::network::ROOT, protocol, dev);
448
449 auto props = ipmi::getAllDbusProperties(
450 bus, ipObjectInfo.second, ipObjectInfo.first,
451 ipmi::network::IP_INTERFACE);
452
453 auto addr = std::get<std::string>(props.at("Address"));
454 if (!addr.empty())
455 return addr;
456 }
457 catch (const sdbusplus::exception::SdBusError&)
458 {}
459 }
460 return std::nullopt;
461 }
findMacAddress(sdbusplus::bus_t & bus,const std::vector<std::string> & ifaces)462 static std::optional<std::string> findMacAddress(
463 sdbusplus::bus_t& bus, const std::vector<std::string>& ifaces)
464 {
465 for (auto& dev : ifaces)
466 {
467 try
468 {
469 auto [path, obj] = ipmi::getDbusObject(
470 bus, ipmi::network::MAC_INTERFACE, ipmi::network::ROOT, dev);
471
472 auto var = ipmi::getDbusProperty(
473 bus, obj, path, ipmi::network::MAC_INTERFACE, "MACAddress");
474
475 auto mac = std::get<std::string>(var);
476 if (!mac.empty())
477 return mac;
478 }
479 catch (const sdbusplus::exception::SdBusError&)
480 {}
481 }
482 return std::nullopt;
483 }
484
getNetworkData(uint8_t lan_param,char * data)485 ipmi_ret_t getNetworkData(uint8_t lan_param, char* data)
486 {
487 static const std::vector<std::string> ifaces{"eth0", "eth1"};
488
489 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
490 ipmi_ret_t rc = IPMI_CC_OK;
491 std::optional<std::string> result;
492
493 switch (static_cast<LanParam>(lan_param))
494 {
495 case LanParam::IP:
496 result = findIpAddress(bus, ipmi::network::IPV4_PROTOCOL, ifaces);
497 lg2::info("Found IP Address: {ADDR}", "ADDR",
498 result.value_or("Not Found"));
499 break;
500
501 case LanParam::IPV6:
502 result = findIpAddress(bus, ipmi::network::IPV6_PROTOCOL, ifaces);
503 lg2::info("Found IPv6 Address: {ADDR}", "ADDR",
504 result.value_or("Not Found"));
505 break;
506
507 case LanParam::MAC:
508 result = findMacAddress(bus, ifaces);
509 lg2::info("Found MAC Address: {ADDR}", "ADDR",
510 result.value_or("Not Found"));
511 break;
512
513 default:
514 return IPMI_CC_PARM_OUT_OF_RANGE;
515 }
516
517 if (!result.has_value())
518 {
519 rc = IPMI_CC_UNSPECIFIED_ERROR;
520 data[0] = '\0';
521 }
522 else
523 {
524 std::strcpy(data, result->c_str());
525 }
526 return rc;
527 }
528
isMultiHostPlatform()529 bool isMultiHostPlatform()
530 {
531 bool platform;
532 if (hostInstances == "0")
533 {
534 platform = false;
535 }
536 else
537 {
538 platform = true;
539 }
540 return platform;
541 }
findFruPathByInterface(sdbusplus::bus_t & dbus,const std::string & interfaceName,std::function<bool (const std::string &)> predicate)542 static std::optional<std::string> findFruPathByInterface(
543 sdbusplus::bus_t& dbus, const std::string& interfaceName,
544 std::function<bool(const std::string&)> predicate)
545 {
546 const int depth = 0;
547 std::vector<std::string> paths;
548
549 auto mapperCall = dbus.new_method_call(
550 "xyz.openbmc_project.ObjectMapper",
551 "/xyz/openbmc_project/object_mapper",
552 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
553 mapperCall.append("/xyz/openbmc_project/inventory/", depth,
554 std::array<const char*, 1>{interfaceName.c_str()});
555
556 try
557 {
558 auto reply = dbus.call(mapperCall);
559 reply.read(paths);
560 }
561 catch (const sdbusplus::exception_t& e)
562 {
563 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
564 return std::nullopt;
565 }
566
567 for (const auto& path : paths)
568 {
569 if (predicate(path))
570 {
571 return path;
572 }
573 }
574 return std::nullopt;
575 }
576
577 // return "" equals failed
getMotherBoardFruPath()578 std::string getMotherBoardFruPath()
579 {
580 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
581
582 bool platform = isMultiHostPlatform();
583 size_t hostPosition;
584 if (platform)
585 getSelectorPosition(hostPosition);
586
587 if (hostPosition == 0)
588 {
589 if (auto path = findFruPathByInterface(
590 dbus, "xyz.openbmc_project.Inventory.Item.Bmc",
591 [](const std::string&) { return true; }))
592 {
593 lg2::info("[1] Found Motherboard: {PATH}", "PATH", *path);
594 return *path;
595 }
596 }
597
598 size_t targetIndex = (hostPosition > 0 ? hostPosition - 1 : 0);
599 auto predNth = [targetIndex,
600 counter = size_t{0}](const std::string& /*path*/) mutable {
601 return (counter++ == targetIndex);
602 };
603
604 if (auto path = findFruPathByInterface(
605 dbus, "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
606 predNth))
607 {
608 lg2::info("[2] Found Motherboard: {PATH}", "PATH", *path);
609 return *path;
610 }
611
612 phosphor::logging::log<phosphor::logging::level::ERR>(
613 "getMotherBoardFruPath: no valid FRU path found");
614 return "";
615 }
616
617 // return "" equals failed
getMotherBoardFruName()618 std::string getMotherBoardFruName()
619 {
620 std::string path = getMotherBoardFruPath();
621 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
622 std::string service = "xyz.openbmc_project.EntityManager";
623
624 try
625 {
626 auto value = ipmi::getDbusProperty(
627 *dbus, service, path, "xyz.openbmc_project.Inventory.Item.Board",
628 "Name");
629 return std::get<std::string>(value);
630 }
631 catch (sdbusplus::exception_t& e)
632 {
633 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
634 return "";
635 }
636 }
637
638 // return code: 0 successful
getFruData(std::string & data,std::string & name)639 int8_t getFruData(std::string& data, std::string& name)
640 {
641 size_t pos;
642 static constexpr const auto depth = 0;
643 std::vector<std::string> paths;
644 std::string machinePath;
645 std::string baseBoard = getMotherBoardFruPath();
646 baseBoard = baseBoard.empty() ? "Baseboard" : baseBoard;
647
648 bool platform = isMultiHostPlatform();
649 if (platform == true)
650 {
651 getSelectorPosition(pos);
652 }
653
654 sd_bus* bus = NULL;
655 int ret = sd_bus_default_system(&bus);
656 if (ret < 0)
657 {
658 phosphor::logging::log<phosphor::logging::level::ERR>(
659 "Failed to connect to system bus",
660 phosphor::logging::entry("ERRNO=0x%X", -ret));
661 sd_bus_unref(bus);
662 return -1;
663 }
664 sdbusplus::bus_t dbus(bus);
665 auto mapperCall = dbus.new_method_call(
666 "xyz.openbmc_project.ObjectMapper",
667 "/xyz/openbmc_project/object_mapper",
668 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
669 static constexpr std::array<const char*, 1> interface = {
670 "xyz.openbmc_project.Inventory.Decorator.Asset"};
671 mapperCall.append("/xyz/openbmc_project/inventory/", depth, interface);
672
673 try
674 {
675 auto reply = dbus.call(mapperCall);
676 reply.read(paths);
677 }
678 catch (sdbusplus::exception_t& e)
679 {
680 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
681 return -1;
682 }
683
684 for (const auto& path : paths)
685 {
686 if (platform == true)
687 {
688 if (pos == BMC_POS)
689 {
690 machinePath = baseBoard;
691 }
692 else
693 {
694 machinePath = "_" + std::to_string(pos);
695 }
696 }
697 else
698 {
699 machinePath = baseBoard;
700 }
701
702 auto found = path.find(machinePath);
703 if (found == std::string::npos)
704 {
705 continue;
706 }
707
708 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
709 std::string service = getService(
710 *dbus, "xyz.openbmc_project.Inventory.Decorator.Asset", path);
711
712 auto Value = ipmi::getDbusProperty(
713 *dbus, service, path,
714 "xyz.openbmc_project.Inventory.Decorator.Asset", name);
715
716 data = std::get<std::string>(Value);
717 return 0;
718 }
719 return -1;
720 }
721
sysConfig(std::vector<std::string> & data,size_t pos)722 int8_t sysConfig(std::vector<std::string>& data, size_t pos)
723 {
724 nlohmann::json sysObj;
725 std::string dimmInfo = KEY_Q_DIMM_INFO + std::to_string(pos);
726 std::string result, typeName;
727 uint8_t res[MAX_BUF];
728
729 /* Get sysConfig data stored in json file */
730 std::ifstream file(JSON_OEM_DATA_FILE);
731 if (file)
732 {
733 file >> sysObj;
734 file.close();
735 }
736 else
737 {
738 phosphor::logging::log<phosphor::logging::level::ERR>(
739 "oemData file not found",
740 phosphor::logging::entry("OEM_DATA_FILE=%s", JSON_OEM_DATA_FILE));
741 return -1;
742 }
743
744 if (sysObj.find(dimmInfo) == sysObj.end())
745 {
746 phosphor::logging::log<phosphor::logging::level::ERR>(
747 "sysconfig key not available",
748 phosphor::logging::entry("SYS_JSON_KEY=%s", dimmInfo.c_str()));
749 return -1;
750 }
751 /* Get dimm type names stored in json file */
752 nlohmann::json dimmObj;
753 std::ifstream dimmFile(JSON_DIMM_TYPE_FILE);
754 if (file)
755 {
756 dimmFile >> dimmObj;
757 dimmFile.close();
758 }
759 else
760 {
761 phosphor::logging::log<phosphor::logging::level::ERR>(
762 "DIMM type names file not found",
763 phosphor::logging::entry("DIMM_TYPE_FILE=%s", JSON_DIMM_TYPE_FILE));
764 return -1;
765 }
766 std::vector<std::string> a;
767 for (auto& j : dimmObj.items())
768 {
769 std::string name = j.key();
770 a.push_back(name);
771 }
772
773 uint8_t len = a.size();
774 for (uint8_t ii = 0; ii < len; ii++)
775 {
776 std::string indKey = std::to_string(ii);
777 std::string speedSize = sysObj[dimmInfo][indKey][DIMM_SPEED];
778 strToBytes(speedSize, res);
779 auto speed = (res[1] << 8 | res[0]);
780 size_t dimmSize = ((res[3] << 8 | res[2]) / 1000);
781
782 if (dimmSize == 0)
783 {
784 std::cerr << "Dimm information not available for slot_" +
785 std::to_string(ii)
786 << std::endl;
787 continue;
788 }
789 std::string type = sysObj[dimmInfo][indKey][DIMM_TYPE];
790 std::string dualInlineMem = sysObj[dimmInfo][indKey][KEY_DIMM_TYPE];
791 strToBytes(type, res);
792 size_t dimmType = res[0];
793 if (dimmVenMap.find(dimmType) == dimmVenMap.end())
794 {
795 typeName = "unknown";
796 }
797 else
798 {
799 typeName = dimmVenMap[dimmType];
800 }
801 result = dualInlineMem + "/" + typeName + "/" + std::to_string(speed) +
802 "MHz" + "/" + std::to_string(dimmSize) + "GB";
803 data.push_back(result);
804 }
805 return 0;
806 }
807
procInfo(std::string & result,size_t pos)808 int8_t procInfo(std::string& result, size_t pos)
809 {
810 std::vector<char> data;
811 uint8_t res[MAX_BUF];
812 std::string procIndex = "00";
813 nlohmann::json proObj;
814 std::string procInfo = KEY_Q_PROC_INFO + std::to_string(pos);
815 /* Get processor data stored in json file */
816 std::ifstream file(JSON_OEM_DATA_FILE);
817 if (file)
818 {
819 file >> proObj;
820 file.close();
821 }
822 else
823 {
824 phosphor::logging::log<phosphor::logging::level::ERR>(
825 "oemData file not found",
826 phosphor::logging::entry("OEM_DATA_FILE=%s", JSON_OEM_DATA_FILE));
827 return -1;
828 }
829 if (proObj.find(procInfo) == proObj.end())
830 {
831 phosphor::logging::log<phosphor::logging::level::ERR>(
832 "processor info key not available",
833 phosphor::logging::entry("PROC_JSON_KEY=%s", procInfo.c_str()));
834 return -1;
835 }
836 std::string procName = proObj[procInfo][procIndex][KEY_PROC_NAME];
837 std::string basicInfo = proObj[procInfo][procIndex][KEY_BASIC_INFO];
838 // Processor Product Name
839 strToBytes(procName, res);
840 data.assign(reinterpret_cast<char*>(&res),
841 reinterpret_cast<char*>(&res) + sizeof(res));
842
843 std::string s(data.begin(), data.end());
844 std::regex regex(" ");
845 std::vector<std::string> productName(
846 std::sregex_token_iterator(s.begin(), s.end(), regex, -1),
847 std::sregex_token_iterator());
848
849 // Processor core and frequency
850 strToBytes(basicInfo, res);
851 uint16_t coreNum = res[0];
852 double procFrequency = (float)(res[4] << 8 | res[3]) / 1000;
853 result = "CPU:" + productName[2] + "/" + std::to_string(procFrequency) +
854 "GHz" + "/" + std::to_string(coreNum) + "c";
855 return 0;
856 }
857
858 typedef struct
859 {
860 uint8_t cur_power_state;
861 uint8_t last_power_event;
862 uint8_t misc_power_state;
863 uint8_t front_panel_button_cap_status;
864 } ipmi_get_chassis_status_t;
865
866 //----------------------------------------------------------------------
867 // Get Debug Frame Info
868 //----------------------------------------------------------------------
ipmiOemDbgGetFrameInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)869 ipmi_ret_t ipmiOemDbgGetFrameInfo(
870 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
871 ipmi_data_len_t data_len, ipmi_context_t)
872 {
873 uint8_t* req = reinterpret_cast<uint8_t*>(request);
874 uint8_t* res = reinterpret_cast<uint8_t*>(response);
875 uint8_t num_frames = debugCardFrameSize;
876
877 std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
878 res[SIZE_IANA_ID] = num_frames;
879 *data_len = SIZE_IANA_ID + 1;
880
881 return ipmi::ccSuccess;
882 }
883
884 //----------------------------------------------------------------------
885 // Get Debug Updated Frames
886 //----------------------------------------------------------------------
ipmiOemDbgGetUpdFrames(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)887 ipmi_ret_t ipmiOemDbgGetUpdFrames(
888 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
889 ipmi_data_len_t data_len, ipmi_context_t)
890 {
891 uint8_t* req = reinterpret_cast<uint8_t*>(request);
892 uint8_t* res = reinterpret_cast<uint8_t*>(response);
893 uint8_t num_updates = 3;
894 *data_len = 4;
895
896 std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
897 res[SIZE_IANA_ID] = num_updates;
898 *data_len = SIZE_IANA_ID + num_updates + 1;
899 res[SIZE_IANA_ID + 1] = 1; // info page update
900 res[SIZE_IANA_ID + 2] = 2; // cri sel update
901 res[SIZE_IANA_ID + 3] = 3; // cri sensor update
902
903 return ipmi::ccSuccess;
904 }
905
906 //----------------------------------------------------------------------
907 // Get Debug POST Description
908 //----------------------------------------------------------------------
ipmiOemDbgGetPostDesc(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)909 ipmi_ret_t ipmiOemDbgGetPostDesc(
910 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
911 ipmi_data_len_t data_len, ipmi_context_t)
912 {
913 uint8_t* req = reinterpret_cast<uint8_t*>(request);
914 uint8_t* res = reinterpret_cast<uint8_t*>(response);
915 uint8_t index = 0;
916 uint8_t next = 0;
917 uint8_t end = 0;
918 uint8_t phase = 0;
919 uint8_t descLen = 0;
920 int ret;
921
922 index = req[3];
923 phase = req[4];
924
925 ret = plat_udbg_get_post_desc(index, &next, phase, &end, &descLen, &res[8]);
926 if (ret)
927 {
928 memcpy(res, req, SIZE_IANA_ID); // IANA ID
929 *data_len = SIZE_IANA_ID;
930 return ipmi::ccUnspecifiedError;
931 }
932
933 memcpy(res, req, SIZE_IANA_ID); // IANA ID
934 res[3] = index;
935 res[4] = next;
936 res[5] = phase;
937 res[6] = end;
938 res[7] = descLen;
939 *data_len = SIZE_IANA_ID + 5 + descLen;
940
941 return ipmi::ccSuccess;
942 }
943
944 //----------------------------------------------------------------------
945 // Get Debug GPIO Description
946 //----------------------------------------------------------------------
ipmiOemDbgGetGpioDesc(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)947 ipmi_ret_t ipmiOemDbgGetGpioDesc(
948 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
949 ipmi_data_len_t data_len, ipmi_context_t)
950 {
951 uint8_t* req = reinterpret_cast<uint8_t*>(request);
952 uint8_t* res = reinterpret_cast<uint8_t*>(response);
953
954 uint8_t index = 0;
955 uint8_t next = 0;
956 uint8_t level = 0;
957 uint8_t pinDef = 0;
958 uint8_t descLen = 0;
959 int ret;
960
961 index = req[3];
962
963 ret = plat_udbg_get_gpio_desc(index, &next, &level, &pinDef, &descLen,
964 &res[8]);
965 if (ret)
966 {
967 memcpy(res, req, SIZE_IANA_ID); // IANA ID
968 *data_len = SIZE_IANA_ID;
969 return ipmi::ccUnspecifiedError;
970 }
971
972 memcpy(res, req, SIZE_IANA_ID); // IANA ID
973 res[3] = index;
974 res[4] = next;
975 res[5] = level;
976 res[6] = pinDef;
977 res[7] = descLen;
978 *data_len = SIZE_IANA_ID + 5 + descLen;
979
980 return ipmi::ccSuccess;
981 }
982
983 //----------------------------------------------------------------------
984 // Get Debug Frame Data
985 //----------------------------------------------------------------------
ipmiOemDbgGetFrameData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)986 ipmi_ret_t ipmiOemDbgGetFrameData(
987 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
988 ipmi_data_len_t data_len, ipmi_context_t)
989 {
990 uint8_t* req = reinterpret_cast<uint8_t*>(request);
991 uint8_t* res = reinterpret_cast<uint8_t*>(response);
992 uint8_t frame;
993 uint8_t page;
994 uint8_t next;
995 uint8_t count;
996 int ret;
997
998 frame = req[3];
999 page = req[4];
1000
1001 ret = plat_udbg_get_frame_data(frame, page, &next, &count, &res[7]);
1002 if (ret)
1003 {
1004 memcpy(res, req, SIZE_IANA_ID); // IANA ID
1005 *data_len = SIZE_IANA_ID;
1006 return ipmi::ccUnspecifiedError;
1007 }
1008
1009 memcpy(res, req, SIZE_IANA_ID); // IANA ID
1010 res[3] = frame;
1011 res[4] = page;
1012 res[5] = next;
1013 res[6] = count;
1014 *data_len = SIZE_IANA_ID + 4 + count;
1015
1016 return ipmi::ccSuccess;
1017 }
1018
1019 //----------------------------------------------------------------------
1020 // Get Debug Control Panel
1021 //----------------------------------------------------------------------
ipmiOemDbgGetCtrlPanel(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)1022 ipmi_ret_t ipmiOemDbgGetCtrlPanel(
1023 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
1024 ipmi_data_len_t data_len, ipmi_context_t)
1025 {
1026 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1027 uint8_t* res = reinterpret_cast<uint8_t*>(response);
1028
1029 uint8_t panel;
1030 uint8_t operation;
1031 uint8_t item;
1032 uint8_t count;
1033 ipmi_ret_t ret;
1034
1035 panel = req[3];
1036 operation = req[4];
1037 item = req[5];
1038
1039 ret = plat_udbg_control_panel(panel, operation, item, &count, &res[3]);
1040
1041 std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
1042 *data_len = SIZE_IANA_ID + count;
1043
1044 return ret;
1045 }
1046
1047 //----------------------------------------------------------------------
1048 // Set Dimm Info (CMD_OEM_SET_DIMM_INFO)
1049 //----------------------------------------------------------------------
ipmiOemSetDimmInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1050 ipmi_ret_t ipmiOemSetDimmInfo(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
1051 ipmi_response_t, ipmi_data_len_t data_len,
1052 ipmi_context_t)
1053 {
1054 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1055
1056 uint8_t index = req[0];
1057 uint8_t type = req[1];
1058 uint16_t speed;
1059 uint32_t size;
1060
1061 memcpy(&speed, &req[2], 2);
1062 memcpy(&size, &req[4], 4);
1063
1064 std::stringstream ss;
1065 ss << std::hex;
1066 ss << std::setw(2) << std::setfill('0') << (int)index;
1067
1068 oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_INDEX] = index;
1069 oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_TYPE] = type;
1070 oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_SPEED] = speed;
1071 oemData[KEY_SYS_CONFIG][ss.str()][KEY_DIMM_SIZE] = size;
1072
1073 flushOemData();
1074
1075 *data_len = 0;
1076
1077 return ipmi::ccSuccess;
1078 }
1079
1080 //----------------------------------------------------------------------
1081 // Get Board ID (CMD_OEM_GET_BOARD_ID)
1082 //----------------------------------------------------------------------
ipmiOemGetBoardID(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1083 ipmi_ret_t ipmiOemGetBoardID(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
1084 ipmi_response_t, ipmi_data_len_t data_len,
1085 ipmi_context_t)
1086 {
1087 /* TODO: Needs to implement this after GPIO implementation */
1088 *data_len = 0;
1089
1090 return ipmi::ccSuccess;
1091 }
1092
1093 //----------------------------------------------------------------------
1094 // Get port 80 record (CMD_OEM_GET_80PORT_RECORD)
1095 //----------------------------------------------------------------------
ipmiOemGet80PortRecord(ipmi::Context::ptr ctx)1096 ipmi::RspType<std::vector<uint8_t>> ipmiOemGet80PortRecord(
1097 ipmi::Context::ptr ctx)
1098 {
1099 auto postCodeService = "xyz.openbmc_project.State.Boot.PostCode" +
1100 std::to_string(ctx->hostIdx + 1);
1101 auto postCodeObjPath = "/xyz/openbmc_project/State/Boot/PostCode" +
1102 std::to_string(ctx->hostIdx + 1);
1103 constexpr auto postCodeInterface =
1104 "xyz.openbmc_project.State.Boot.PostCode";
1105 const static uint16_t lastestPostCodeIndex = 1;
1106 constexpr const auto maxPostCodeLen =
1107 224; // The length must be lower than IPMB limitation
1108 size_t startIndex = 0;
1109
1110 std::vector<std::tuple<uint64_t, std::vector<uint8_t>>> postCodes;
1111 std::vector<uint8_t> resData;
1112
1113 auto conn = getSdBus();
1114 /* Get the post codes by calling GetPostCodes method */
1115 auto msg =
1116 conn->new_method_call(postCodeService.c_str(), postCodeObjPath.c_str(),
1117 postCodeInterface, "GetPostCodes");
1118 msg.append(lastestPostCodeIndex);
1119
1120 try
1121 {
1122 auto reply = conn->call(msg);
1123 reply.read(postCodes);
1124 }
1125 catch (const sdbusplus::exception::SdBusError& e)
1126 {
1127 phosphor::logging::log<phosphor::logging::level::ERR>(
1128 "IPMI Get80PortRecord Failed in call method",
1129 phosphor::logging::entry("ERROR=%s", e.what()));
1130 return ipmi::responseUnspecifiedError();
1131 }
1132
1133 /* Get post code data */
1134 for (size_t i = 0; i < postCodes.size(); ++i)
1135 {
1136 uint64_t primaryPostCode = std::get<uint64_t>(postCodes[i]);
1137 for (int j = postCodeSize - 1; j >= 0; --j)
1138 {
1139 uint8_t postCode =
1140 ((primaryPostCode >> (sizeof(uint64_t) * j)) & 0xFF);
1141 resData.emplace_back(postCode);
1142 }
1143 }
1144
1145 std::vector<uint8_t> response;
1146 if (resData.size() > maxPostCodeLen)
1147 {
1148 startIndex = resData.size() - maxPostCodeLen;
1149 }
1150
1151 response.assign(resData.begin() + startIndex, resData.end());
1152
1153 return ipmi::responseSuccess(response);
1154 }
1155
1156 //----------------------------------------------------------------------
1157 // Set Boot Order (CMD_OEM_SET_BOOT_ORDER)
1158 //----------------------------------------------------------------------
ipmiOemSetBootOrder(ipmi::Context::ptr ctx,std::vector<uint8_t> bootSeq)1159 ipmi::RspType<std::vector<uint8_t>> ipmiOemSetBootOrder(
1160 ipmi::Context::ptr ctx, std::vector<uint8_t> bootSeq)
1161 {
1162 size_t len = bootSeq.size();
1163
1164 if (len != SIZE_BOOT_ORDER)
1165 {
1166 phosphor::logging::log<phosphor::logging::level::ERR>(
1167 "Invalid Boot order length received");
1168 return ipmi::responseReqDataLenInvalid();
1169 }
1170
1171 std::optional<size_t> hostId = findHost(ctx->hostIdx);
1172
1173 if (!hostId)
1174 {
1175 phosphor::logging::log<phosphor::logging::level::ERR>(
1176 "Invalid Host Id received");
1177 return ipmi::responseInvalidCommand();
1178 }
1179 auto [bootObjPath, hostName] = ipmi::boot::objPath(*hostId);
1180
1181 ipmi::boot::setBootOrder(bootObjPath, bootSeq, hostName);
1182
1183 return ipmi::responseSuccess(bootSeq);
1184 }
1185
1186 //----------------------------------------------------------------------
1187 // Get Boot Order (CMD_OEM_GET_BOOT_ORDER)
1188 //----------------------------------------------------------------------
ipmiOemGetBootOrder(ipmi::Context::ptr ctx)1189 ipmi::RspType<std::vector<uint8_t>> ipmiOemGetBootOrder(ipmi::Context::ptr ctx)
1190 {
1191 std::vector<uint8_t> bootSeq;
1192
1193 std::optional<size_t> hostId = findHost(ctx->hostIdx);
1194
1195 if (!hostId)
1196 {
1197 phosphor::logging::log<phosphor::logging::level::ERR>(
1198 "Invalid Host Id received");
1199 return ipmi::responseInvalidCommand();
1200 }
1201 auto [bootObjPath, hostName] = ipmi::boot::objPath(*hostId);
1202
1203 ipmi::boot::getBootOrder(bootObjPath, bootSeq, hostName);
1204
1205 return ipmi::responseSuccess(bootSeq);
1206 }
1207 // Set Machine Config Info (CMD_OEM_SET_MACHINE_CONFIG_INFO)
1208 //----------------------------------------------------------------------
ipmiOemSetMachineCfgInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1209 ipmi_ret_t ipmiOemSetMachineCfgInfo(ipmi_netfn_t, ipmi_cmd_t,
1210 ipmi_request_t request, ipmi_response_t,
1211 ipmi_data_len_t data_len, ipmi_context_t)
1212 {
1213 machineConfigInfo_t* req = reinterpret_cast<machineConfigInfo_t*>(request);
1214 uint8_t len = *data_len;
1215
1216 *data_len = 0;
1217
1218 if (len < sizeof(machineConfigInfo_t))
1219 {
1220 phosphor::logging::log<phosphor::logging::level::ERR>(
1221 "Invalid machine configuration length received");
1222 return ipmi::ccReqDataLenInvalid;
1223 }
1224
1225 if (req->chassis_type >= sizeof(chassisType) / sizeof(uint8_t*))
1226 oemData[KEY_MC_CONFIG][KEY_MC_CHAS_TYPE] = "UNKNOWN";
1227 else
1228 oemData[KEY_MC_CONFIG][KEY_MC_CHAS_TYPE] =
1229 chassisType[req->chassis_type];
1230
1231 if (req->mb_type >= sizeof(mbType) / sizeof(uint8_t*))
1232 oemData[KEY_MC_CONFIG][KEY_MC_MB_TYPE] = "UNKNOWN";
1233 else
1234 oemData[KEY_MC_CONFIG][KEY_MC_MB_TYPE] = mbType[req->mb_type];
1235
1236 oemData[KEY_MC_CONFIG][KEY_MC_PROC_CNT] = req->proc_cnt;
1237 oemData[KEY_MC_CONFIG][KEY_MC_MEM_CNT] = req->mem_cnt;
1238 oemData[KEY_MC_CONFIG][KEY_MC_HDD35_CNT] = req->hdd35_cnt;
1239 oemData[KEY_MC_CONFIG][KEY_MC_HDD25_CNT] = req->hdd25_cnt;
1240
1241 if (req->riser_type >= sizeof(riserType) / sizeof(uint8_t*))
1242 oemData[KEY_MC_CONFIG][KEY_MC_RSR_TYPE] = "UNKNOWN";
1243 else
1244 oemData[KEY_MC_CONFIG][KEY_MC_RSR_TYPE] = riserType[req->riser_type];
1245
1246 oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC] = {};
1247 int i = 0;
1248 if (req->pcie_card_loc & BIT_0)
1249 oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT1";
1250 if (req->pcie_card_loc & BIT_1)
1251 oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT2";
1252 if (req->pcie_card_loc & BIT_2)
1253 oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT3";
1254 if (req->pcie_card_loc & BIT_3)
1255 oemData[KEY_MC_CONFIG][KEY_MC_PCIE_LOC][i++] = "SLOT4";
1256
1257 if (req->slot1_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
1258 oemData[KEY_MC_CONFIG][KEY_MC_SLOT1_TYPE] = "UNKNOWN";
1259 else
1260 oemData[KEY_MC_CONFIG][KEY_MC_SLOT1_TYPE] =
1261 pcieType[req->slot1_pcie_type];
1262
1263 if (req->slot2_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
1264 oemData[KEY_MC_CONFIG][KEY_MC_SLOT2_TYPE] = "UNKNOWN";
1265 else
1266 oemData[KEY_MC_CONFIG][KEY_MC_SLOT2_TYPE] =
1267 pcieType[req->slot2_pcie_type];
1268
1269 if (req->slot3_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
1270 oemData[KEY_MC_CONFIG][KEY_MC_SLOT3_TYPE] = "UNKNOWN";
1271 else
1272 oemData[KEY_MC_CONFIG][KEY_MC_SLOT3_TYPE] =
1273 pcieType[req->slot3_pcie_type];
1274
1275 if (req->slot4_pcie_type >= sizeof(pcieType) / sizeof(uint8_t*))
1276 oemData[KEY_MC_CONFIG][KEY_MC_SLOT4_TYPE] = "UNKNOWN";
1277 else
1278 oemData[KEY_MC_CONFIG][KEY_MC_SLOT4_TYPE] =
1279 pcieType[req->slot4_pcie_type];
1280
1281 oemData[KEY_MC_CONFIG][KEY_MC_AEP_CNT] = req->aep_mem_cnt;
1282
1283 flushOemData();
1284
1285 return ipmi::ccSuccess;
1286 }
1287
1288 //----------------------------------------------------------------------
1289 // Set POST start (CMD_OEM_SET_POST_START)
1290 //----------------------------------------------------------------------
ipmiOemSetPostStart(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1291 ipmi_ret_t ipmiOemSetPostStart(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
1292 ipmi_response_t, ipmi_data_len_t data_len,
1293 ipmi_context_t)
1294 {
1295 phosphor::logging::log<phosphor::logging::level::INFO>("POST Start Event");
1296
1297 /* Do nothing, return success */
1298 *data_len = 0;
1299 return ipmi::ccSuccess;
1300 }
1301
1302 //----------------------------------------------------------------------
1303 // Set POST End (CMD_OEM_SET_POST_END)
1304 //----------------------------------------------------------------------
ipmiOemSetPostEnd(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1305 ipmi_ret_t ipmiOemSetPostEnd(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
1306 ipmi_response_t, ipmi_data_len_t data_len,
1307 ipmi_context_t)
1308 {
1309 struct timespec ts;
1310
1311 phosphor::logging::log<phosphor::logging::level::INFO>("POST End Event");
1312
1313 *data_len = 0;
1314
1315 // Timestamp post end time.
1316 clock_gettime(CLOCK_REALTIME, &ts);
1317 oemData[KEY_TS_SLED] = ts.tv_sec;
1318 flushOemData();
1319
1320 // Sync time with system
1321 // TODO: Add code for syncing time
1322
1323 return ipmi::ccSuccess;
1324 }
1325
1326 //----------------------------------------------------------------------
1327 // Set PPIN Info (CMD_OEM_SET_PPIN_INFO)
1328 //----------------------------------------------------------------------
1329 // Inform BMC about PPIN data of 8 bytes for each CPU
1330 //
1331 // Request:
1332 // Byte 1:8 – CPU0 PPIN data
1333 // Optional:
1334 // Byte 9:16 – CPU1 PPIN data
1335 //
1336 // Response:
1337 // Byte 1 – Completion Code
ipmiOemSetPPINInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1338 ipmi_ret_t ipmiOemSetPPINInfo(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
1339 ipmi_response_t, ipmi_data_len_t data_len,
1340 ipmi_context_t)
1341 {
1342 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1343 std::string ppinStr;
1344 int len;
1345
1346 if (*data_len > SIZE_CPU_PPIN * 2)
1347 len = SIZE_CPU_PPIN * 2;
1348 else
1349 len = *data_len;
1350 *data_len = 0;
1351
1352 ppinStr = bytesToStr(req, len);
1353 oemData[KEY_PPIN_INFO] = ppinStr.c_str();
1354 flushOemData();
1355
1356 return ipmi::ccSuccess;
1357 }
1358
1359 //----------------------------------------------------------------------
1360 // Set ADR Trigger (CMD_OEM_SET_ADR_TRIGGER)
1361 //----------------------------------------------------------------------
ipmiOemSetAdrTrigger(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1362 ipmi_ret_t ipmiOemSetAdrTrigger(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
1363 ipmi_response_t, ipmi_data_len_t data_len,
1364 ipmi_context_t)
1365 {
1366 /* Do nothing, return success */
1367 *data_len = 0;
1368 return ipmi::ccSuccess;
1369 }
1370
1371 // Helper function to set guid at offset in EEPROM
setGUID(off_t offset,uint8_t * guid)1372 [[maybe_unused]] static int setGUID(off_t offset, uint8_t* guid)
1373 {
1374 int fd = -1;
1375 ssize_t len;
1376 int ret = 0;
1377 std::string eepromPath = FRU_EEPROM;
1378
1379 // find the eeprom path of MB FRU
1380 auto device = getMbFruDevice();
1381 if (device)
1382 {
1383 auto [bus, address] = *device;
1384 std::stringstream ss;
1385 ss << "/sys/bus/i2c/devices/" << static_cast<int>(bus) << "-"
1386 << std::setw(4) << std::setfill('0') << std::hex
1387 << static_cast<int>(address) << "/eeprom";
1388 eepromPath = ss.str();
1389 }
1390
1391 errno = 0;
1392
1393 // Check if file is present
1394 if (access(eepromPath.c_str(), F_OK) == -1)
1395 {
1396 std::cerr << "Unable to access: " << eepromPath << std::endl;
1397 return errno;
1398 }
1399
1400 // Open the file
1401 fd = open(eepromPath.c_str(), O_WRONLY);
1402 if (fd == -1)
1403 {
1404 std::cerr << "Unable to open: " << eepromPath << std::endl;
1405 return errno;
1406 }
1407
1408 // seek to the offset
1409 lseek(fd, offset, SEEK_SET);
1410
1411 // Write bytes to location
1412 len = write(fd, guid, GUID_SIZE);
1413 if (len != GUID_SIZE)
1414 {
1415 phosphor::logging::log<phosphor::logging::level::ERR>(
1416 "GUID write data to EEPROM failed");
1417 ret = errno;
1418 }
1419
1420 close(fd);
1421 return ret;
1422 }
1423
1424 //----------------------------------------------------------------------
1425 // Set System GUID (CMD_OEM_SET_SYSTEM_GUID)
1426 //----------------------------------------------------------------------
1427 #if BIC_ENABLED
ipmiOemSetSystemGuid(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)1428 ipmi::RspType<> ipmiOemSetSystemGuid(ipmi::Context::ptr ctx,
1429 std::vector<uint8_t> reqData)
1430 {
1431 std::vector<uint8_t> respData;
1432
1433 if (reqData.size() != GUID_SIZE) // 16bytes
1434 {
1435 return ipmi::responseReqDataLenInvalid();
1436 }
1437
1438 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
1439
1440 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
1441 return ipmi::responseUnspecifiedError();
1442
1443 return ipmi::responseSuccess();
1444 }
1445
1446 #else
ipmiOemSetSystemGuid(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1447 ipmi_ret_t ipmiOemSetSystemGuid(ipmi_netfn_t, ipmi_cmd_t,
1448 ipmi_request_t request, ipmi_response_t,
1449 ipmi_data_len_t data_len, ipmi_context_t)
1450 {
1451 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1452
1453 if (*data_len != GUID_SIZE) // 16bytes
1454 {
1455 *data_len = 0;
1456 return ipmi::ccReqDataLenInvalid;
1457 }
1458
1459 *data_len = 0;
1460
1461 if (setGUID(OFFSET_SYS_GUID, req))
1462 {
1463 return ipmi::ccUnspecifiedError;
1464 }
1465 return ipmi::ccSuccess;
1466 }
1467 #endif
1468
1469 //----------------------------------------------------------------------
1470 // Set Bios Flash Info (CMD_OEM_SET_BIOS_FLASH_INFO)
1471 //----------------------------------------------------------------------
ipmiOemSetBiosFlashInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1472 ipmi_ret_t ipmiOemSetBiosFlashInfo(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t,
1473 ipmi_response_t, ipmi_data_len_t data_len,
1474 ipmi_context_t)
1475 {
1476 /* Do nothing, return success */
1477 *data_len = 0;
1478 return ipmi::ccSuccess;
1479 }
1480
1481 //----------------------------------------------------------------------
1482 // Set PPR (CMD_OEM_SET_PPR)
1483 //----------------------------------------------------------------------
ipmiOemSetPpr(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)1484 ipmi_ret_t ipmiOemSetPpr(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
1485 ipmi_response_t, ipmi_data_len_t data_len,
1486 ipmi_context_t)
1487 {
1488 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1489 uint8_t pprCnt, pprAct, pprIndex;
1490 uint8_t selParam = req[0];
1491 uint8_t len = *data_len;
1492 std::stringstream ss;
1493 std::string str;
1494
1495 *data_len = 0;
1496
1497 switch (selParam)
1498 {
1499 case PPR_ACTION:
1500 if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) ==
1501 oemData[KEY_PPR].end())
1502 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1503
1504 pprCnt = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
1505 if (pprCnt == 0)
1506 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
1507
1508 pprAct = req[1];
1509 /* Check if ppr is enabled or disabled */
1510 if (!(pprAct & 0x80))
1511 pprAct = 0;
1512
1513 oemData[KEY_PPR][KEY_PPR_ACTION] = pprAct;
1514 break;
1515 case PPR_ROW_COUNT:
1516 if (req[1] > 100)
1517 return ipmi::ccParmOutOfRange;
1518
1519 oemData[KEY_PPR][KEY_PPR_ROW_COUNT] = req[1];
1520 break;
1521 case PPR_ROW_ADDR:
1522 pprIndex = req[1];
1523 if (pprIndex > 100)
1524 return ipmi::ccParmOutOfRange;
1525
1526 if (len < PPR_ROW_ADDR_LEN + 1)
1527 {
1528 phosphor::logging::log<phosphor::logging::level::ERR>(
1529 "Invalid PPR Row Address length received");
1530 return ipmi::ccReqDataLenInvalid;
1531 }
1532
1533 ss << std::hex;
1534 ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1535
1536 oemData[KEY_PPR][ss.str()][KEY_PPR_INDEX] = pprIndex;
1537
1538 str = bytesToStr(&req[1], PPR_ROW_ADDR_LEN);
1539 oemData[KEY_PPR][ss.str()][KEY_PPR_ROW_ADDR] = str.c_str();
1540 break;
1541 case PPR_HISTORY_DATA:
1542 pprIndex = req[1];
1543 if (pprIndex > 100)
1544 return ipmi::ccParmOutOfRange;
1545
1546 if (len < PPR_HST_DATA_LEN + 1)
1547 {
1548 phosphor::logging::log<phosphor::logging::level::ERR>(
1549 "Invalid PPR history data length received");
1550 return ipmi::ccReqDataLenInvalid;
1551 }
1552
1553 ss << std::hex;
1554 ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1555
1556 oemData[KEY_PPR][ss.str()][KEY_PPR_INDEX] = pprIndex;
1557
1558 str = bytesToStr(&req[1], PPR_HST_DATA_LEN);
1559 oemData[KEY_PPR][ss.str()][KEY_PPR_HST_DATA] = str.c_str();
1560 break;
1561 default:
1562 return ipmi::ccParmOutOfRange;
1563 break;
1564 }
1565
1566 flushOemData();
1567
1568 return ipmi::ccSuccess;
1569 }
1570
1571 //----------------------------------------------------------------------
1572 // Get PPR (CMD_OEM_GET_PPR)
1573 //----------------------------------------------------------------------
ipmiOemGetPpr(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)1574 ipmi_ret_t ipmiOemGetPpr(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
1575 ipmi_response_t response, ipmi_data_len_t data_len,
1576 ipmi_context_t)
1577 {
1578 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1579 uint8_t* res = reinterpret_cast<uint8_t*>(response);
1580 uint8_t pprCnt, pprIndex;
1581 uint8_t selParam = req[0];
1582 std::stringstream ss;
1583 std::string str;
1584
1585 /* Any failure will return zero length data */
1586 *data_len = 0;
1587
1588 switch (selParam)
1589 {
1590 case PPR_ACTION:
1591 res[0] = 0;
1592 *data_len = 1;
1593
1594 if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) !=
1595 oemData[KEY_PPR].end())
1596 {
1597 pprCnt = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
1598 if (pprCnt != 0)
1599 {
1600 if (oemData[KEY_PPR].find(KEY_PPR_ACTION) !=
1601 oemData[KEY_PPR].end())
1602 {
1603 res[0] = oemData[KEY_PPR][KEY_PPR_ACTION];
1604 }
1605 }
1606 }
1607 break;
1608 case PPR_ROW_COUNT:
1609 res[0] = 0;
1610 *data_len = 1;
1611 if (oemData[KEY_PPR].find(KEY_PPR_ROW_COUNT) !=
1612 oemData[KEY_PPR].end())
1613 res[0] = oemData[KEY_PPR][KEY_PPR_ROW_COUNT];
1614 break;
1615 case PPR_ROW_ADDR:
1616 pprIndex = req[1];
1617 if (pprIndex > 100)
1618 return ipmi::ccParmOutOfRange;
1619
1620 ss << std::hex;
1621 ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1622
1623 if (oemData[KEY_PPR].find(ss.str()) == oemData[KEY_PPR].end())
1624 return ipmi::ccParmOutOfRange;
1625
1626 if (oemData[KEY_PPR][ss.str()].find(KEY_PPR_ROW_ADDR) ==
1627 oemData[KEY_PPR][ss.str()].end())
1628 return ipmi::ccParmOutOfRange;
1629
1630 str = oemData[KEY_PPR][ss.str()][KEY_PPR_ROW_ADDR];
1631 *data_len = strToBytes(str, res);
1632 break;
1633 case PPR_HISTORY_DATA:
1634 pprIndex = req[1];
1635 if (pprIndex > 100)
1636 return ipmi::ccParmOutOfRange;
1637
1638 ss << std::hex;
1639 ss << std::setw(2) << std::setfill('0') << (int)pprIndex;
1640
1641 if (oemData[KEY_PPR].find(ss.str()) == oemData[KEY_PPR].end())
1642 return ipmi::ccParmOutOfRange;
1643
1644 if (oemData[KEY_PPR][ss.str()].find(KEY_PPR_HST_DATA) ==
1645 oemData[KEY_PPR][ss.str()].end())
1646 return ipmi::ccParmOutOfRange;
1647
1648 str = oemData[KEY_PPR][ss.str()][KEY_PPR_HST_DATA];
1649 *data_len = strToBytes(str, res);
1650 break;
1651 default:
1652 return ipmi::ccParmOutOfRange;
1653 break;
1654 }
1655
1656 return ipmi::ccSuccess;
1657 }
1658
1659 //----------------------------------------------------------------------
1660 // Get FRU ID (CMD_OEM_GET_FRU_ID)
1661 //----------------------------------------------------------------------
1662 // BIOS issues this command to retrieve the FRU ID using the bus and slave
1663 // address. FRU IDs are dynamic based on entity-manager probe orders so
1664 // this allows a look-up based on known data from the host to identify a
1665 // specific FRU.
1666 //
1667 // netfn 0x30, cmd 0x84
1668 //
1669 // Request:
1670 // - Byte 1: bus
1671 // - Byte 2: slave address
1672 //
1673 // Response:
1674 // - Byte 1 - Completion Code
1675 // - Byte 2 - FRU ID
1676
ipmiOemGetFruId(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)1677 ipmi_ret_t ipmiOemGetFruId(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
1678 ipmi_response_t response, ipmi_data_len_t data_len,
1679 ipmi_context_t)
1680 {
1681 uint8_t* req = reinterpret_cast<uint8_t*>(request);
1682 uint8_t* res = reinterpret_cast<uint8_t*>(response);
1683 auto bus = sdbusplus::bus::new_default();
1684
1685 const uint32_t targetBus = req[0];
1686 const uint32_t targetAddress = req[1];
1687 *data_len = 1;
1688
1689 const std::string service = "xyz.openbmc_project.FruDevice";
1690 const std::string rootPath = "/xyz/openbmc_project/FruDevice";
1691
1692 auto method = bus.new_method_call(service.c_str(), "/",
1693 "org.freedesktop.DBus.ObjectManager",
1694 "GetManagedObjects");
1695
1696 auto reply = bus.call(method);
1697
1698 using PropertyValue = std::variant<std::string, uint32_t, int32_t, bool>;
1699
1700 std::map<sdbusplus::message::object_path,
1701 std::map<std::string, std::map<std::string, PropertyValue>>>
1702 objects;
1703
1704 reply.read(objects);
1705
1706 int index = 0;
1707 bool found = false;
1708
1709 for (const auto& [path, interfaces] : objects)
1710 {
1711 if (path.str.find(rootPath) != 0)
1712 continue;
1713
1714 auto it = interfaces.find("xyz.openbmc_project.FruDevice");
1715 if (it != interfaces.end())
1716 {
1717 const auto& properties = it->second;
1718
1719 auto busIt = properties.find("BUS");
1720 auto addrIt = properties.find("ADDRESS");
1721
1722 if (busIt != properties.end() && addrIt != properties.end())
1723 {
1724 try
1725 {
1726 uint32_t busValue = std::get<uint32_t>(busIt->second);
1727 uint32_t addrValue = std::get<uint32_t>(addrIt->second);
1728
1729 if (busValue == targetBus && addrValue == targetAddress)
1730 {
1731 std::cout
1732 << "Match found at index: " << index << std::endl;
1733 std::cout << "Path: " << path.str << std::endl;
1734 res[0] = index;
1735 found = true;
1736 break;
1737 }
1738 }
1739 catch (const std::bad_variant_access& e)
1740 {
1741 std::cerr << "️Unexpected property type at index " << index
1742 << std::endl;
1743 return -1;
1744 }
1745 }
1746 index++;
1747 }
1748 }
1749
1750 if (!found)
1751 {
1752 std::cout << "No matching FRU device found for BUS=" << targetBus
1753 << " and ADDRESS=" << targetAddress << std::endl;
1754 return -1;
1755 }
1756
1757 return ipmi::ccSuccess;
1758 }
1759
1760 /* FB OEM QC Commands */
1761
1762 //----------------------------------------------------------------------
1763 // Set Proc Info (CMD_OEM_Q_SET_PROC_INFO)
1764 //----------------------------------------------------------------------
1765 //"Request:
1766 // Byte 1:3 – Manufacturer ID – XXYYZZ h, LSB first
1767 // Byte 4 – Processor Index, 0 base
1768 // Byte 5 – Parameter Selector
1769 // Byte 6..N – Configuration parameter data (see below for Parameters
1770 // of Processor Information)
1771 // Response:
1772 // Byte 1 – Completion code
1773 //
1774 // Parameter#1: (Processor Product Name)
1775 //
1776 // Byte 1..48 –Product name(ASCII code)
1777 // Ex. Intel(R) Xeon(R) CPU E5-2685 v3 @ 2.60GHz
1778 //
1779 // Param#2: Processor Basic Information
1780 // Byte 1 – Core Number
1781 // Byte 2 – Thread Number (LSB)
1782 // Byte 3 – Thread Number (MSB)
1783 // Byte 4 – Processor frequency in MHz (LSB)
1784 // Byte 5 – Processor frequency in MHz (MSB)
1785 // Byte 6..7 – Revision
1786 //
1787
ipmiOemQSetProcInfo(ipmi::Context::ptr ctx,uint8_t,uint8_t,uint8_t,uint8_t procIndex,uint8_t paramSel,std::vector<uint8_t> request)1788 ipmi::RspType<> ipmiOemQSetProcInfo(
1789 ipmi::Context::ptr ctx, uint8_t, uint8_t, uint8_t, uint8_t procIndex,
1790 uint8_t paramSel, std::vector<uint8_t> request)
1791 {
1792 uint8_t numParam = sizeof(cpuInfoKey) / sizeof(uint8_t*);
1793 std::stringstream ss;
1794 std::string str;
1795 uint8_t len = request.size();
1796 auto hostId = findHost(ctx->hostIdx);
1797 if (!hostId)
1798 {
1799 phosphor::logging::log<phosphor::logging::level::ERR>(
1800 "Invalid Host Id received");
1801 return ipmi::responseInvalidCommand();
1802 }
1803 std::string procInfo = KEY_Q_PROC_INFO + std::to_string(*hostId);
1804 /* check for requested data params */
1805 if (len < 5 || paramSel < 1 || paramSel >= numParam)
1806 {
1807 phosphor::logging::log<phosphor::logging::level::ERR>(
1808 "Invalid parameter received");
1809 return ipmi::responseParmOutOfRange();
1810 }
1811 ss << std::hex;
1812 ss << std::setw(2) << std::setfill('0') << (int)procIndex;
1813 oemData[procInfo][ss.str()][KEY_PROC_INDEX] = procIndex;
1814 str = bytesToStr(request.data(), len);
1815 oemData[procInfo][ss.str()][cpuInfoKey[paramSel]] = str.c_str();
1816 flushOemData();
1817 return ipmi::responseSuccess();
1818 }
1819
1820 //----------------------------------------------------------------------
1821 // Get Proc Info (CMD_OEM_Q_GET_PROC_INFO)
1822 //----------------------------------------------------------------------
1823 // Request:
1824 // Byte 1:3 – Manufacturer ID – XXYYZZ h, LSB first
1825 // Byte 4 – Processor Index, 0 base
1826 // Byte 5 – Parameter Selector
1827 // Response:
1828 // Byte 1 – Completion code
1829 // Byte 2..N – Configuration Parameter Data (see below for Parameters
1830 // of Processor Information)
1831 //
1832 // Parameter#1: (Processor Product Name)
1833 //
1834 // Byte 1..48 –Product name(ASCII code)
1835 // Ex. Intel(R) Xeon(R) CPU E5-2685 v3 @ 2.60GHz
1836 //
1837 // Param#2: Processor Basic Information
1838 // Byte 1 – Core Number
1839 // Byte 2 – Thread Number (LSB)
1840 // Byte 3 – Thread Number (MSB)
1841 // Byte 4 – Processor frequency in MHz (LSB)
1842 // Byte 5 – Processor frequency in MHz (MSB)
1843 // Byte 6..7 – Revision
1844 //
1845
ipmiOemQGetProcInfo(ipmi::Context::ptr ctx,uint8_t,uint8_t,uint8_t,uint8_t procIndex,uint8_t paramSel)1846 ipmi::RspType<std::vector<uint8_t>> ipmiOemQGetProcInfo(
1847 ipmi::Context::ptr ctx, uint8_t, uint8_t, uint8_t, uint8_t procIndex,
1848 uint8_t paramSel)
1849 {
1850 uint8_t numParam = sizeof(cpuInfoKey) / sizeof(uint8_t*);
1851 std::stringstream ss;
1852 std::string str;
1853 uint8_t res[MAX_BUF];
1854 auto hostId = findHost(ctx->hostIdx);
1855 if (!hostId)
1856 {
1857 phosphor::logging::log<phosphor::logging::level::ERR>(
1858 "Invalid Host Id received");
1859 return ipmi::responseInvalidCommand();
1860 }
1861 std::string procInfo = KEY_Q_PROC_INFO + std::to_string(*hostId);
1862 if (paramSel < 1 || paramSel >= numParam)
1863 {
1864 phosphor::logging::log<phosphor::logging::level::ERR>(
1865 "Invalid parameter received");
1866 return ipmi::responseParmOutOfRange();
1867 }
1868 ss << std::hex;
1869 ss << std::setw(2) << std::setfill('0') << (int)procIndex;
1870 if (oemData[procInfo].find(ss.str()) == oemData[procInfo].end())
1871 return ipmi::responseCommandNotAvailable();
1872 if (oemData[procInfo][ss.str()].find(cpuInfoKey[paramSel]) ==
1873 oemData[procInfo][ss.str()].end())
1874 return ipmi::responseCommandNotAvailable();
1875 str = oemData[procInfo][ss.str()][cpuInfoKey[paramSel]];
1876 int dataLen = strToBytes(str, res);
1877 std::vector<uint8_t> response(&res[0], &res[dataLen]);
1878 return ipmi::responseSuccess(response);
1879 }
1880
1881 //----------------------------------------------------------------------
1882 // Set Dimm Info (CMD_OEM_Q_SET_DIMM_INFO)
1883 //----------------------------------------------------------------------
1884 // Request:
1885 // Byte 1:3 – Manufacturer ID – XXYYZZh, LSB first
1886 // Byte 4 – DIMM Index, 0 base
1887 // Byte 5 – Parameter Selector
1888 // Byte 6..N – Configuration parameter data (see below for Parameters
1889 // of DIMM Information)
1890 // Response:
1891 // Byte 1 – Completion code
1892 //
1893 // Param#1 (DIMM Location):
1894 // Byte 1 – DIMM Present
1895 // Byte 1 – DIMM Present
1896 // 01h – Present
1897 // FFh – Not Present
1898 // Byte 2 – Node Number, 0 base
1899 // Byte 3 – Channel Number , 0 base
1900 // Byte 4 – DIMM Number , 0 base
1901 //
1902 // Param#2 (DIMM Type):
1903 // Byte 1 – DIMM Type
1904 // Bit [7:6]
1905 // For DDR3
1906 // 00 – Normal Voltage (1.5V)
1907 // 01 – Ultra Low Voltage (1.25V)
1908 // 10 – Low Voltage (1.35V)
1909 // 11 – Reserved
1910 // For DDR4
1911 // 00 – Reserved
1912 // 01 – Reserved
1913 // 10 – Reserved
1914 // 11 – Normal Voltage (1.2V)
1915 // Bit [5:0]
1916 // 0x00 – SDRAM
1917 // 0x01 – DDR-1 RAM
1918 // 0x02 – Rambus
1919 // 0x03 – DDR-2 RAM
1920 // 0x04 – FBDIMM
1921 // 0x05 – DDR-3 RAM
1922 // 0x06 – DDR-4 RAM
1923 //
1924 // Param#3 (DIMM Speed):
1925 // Byte 1..2 – DIMM speed in MHz, LSB
1926 // Byte 3..6 – DIMM size in Mbytes, LSB
1927 //
1928 // Param#4 (Module Part Number):
1929 // Byte 1..20 –Module Part Number (JEDEC Standard No. 21-C)
1930 //
1931 // Param#5 (Module Serial Number):
1932 // Byte 1..4 –Module Serial Number (JEDEC Standard No. 21-C)
1933 //
1934 // Param#6 (Module Manufacturer ID):
1935 // Byte 1 - Module Manufacturer ID, LSB
1936 // Byte 2 - Module Manufacturer ID, MSB
1937 //
ipmiOemQSetDimmInfo(ipmi::Context::ptr ctx,uint8_t,uint8_t,uint8_t,uint8_t dimmIndex,uint8_t paramSel,std::vector<uint8_t> request)1938 ipmi::RspType<> ipmiOemQSetDimmInfo(
1939 ipmi::Context::ptr ctx, uint8_t, uint8_t, uint8_t, uint8_t dimmIndex,
1940 uint8_t paramSel, std::vector<uint8_t> request)
1941 {
1942 uint8_t numParam = sizeof(dimmInfoKey) / sizeof(uint8_t*);
1943 std::stringstream ss;
1944 std::string str;
1945 uint8_t len = request.size();
1946 std::string dimmType;
1947 readDimmType(dimmType, dimmIndex);
1948 auto hostId = findHost(ctx->hostIdx);
1949 if (!hostId)
1950 {
1951 phosphor::logging::log<phosphor::logging::level::ERR>(
1952 "Invalid Host Id received");
1953 return ipmi::responseInvalidCommand();
1954 }
1955
1956 std::string dimmInfo = KEY_Q_DIMM_INFO + std::to_string(*hostId);
1957
1958 if (len < 3 || paramSel < 1 || paramSel >= numParam)
1959 {
1960 phosphor::logging::log<phosphor::logging::level::ERR>(
1961 "Invalid parameter received");
1962 return ipmi::responseParmOutOfRange();
1963 }
1964
1965 ss << std::hex;
1966 ss << (int)dimmIndex;
1967 oemData[dimmInfo][ss.str()][KEY_DIMM_INDEX] = dimmIndex;
1968 oemData[dimmInfo][ss.str()][KEY_DIMM_TYPE] = dimmType;
1969 str = bytesToStr(request.data(), len);
1970 oemData[dimmInfo][ss.str()][dimmInfoKey[paramSel]] = str.c_str();
1971 flushOemData();
1972 return ipmi::responseSuccess();
1973 }
1974
1975 // Get Dimm Info (CMD_OEM_Q_GET_DIMM_INFO)
1976 //----------------------------------------------------------------------
1977 // Request:
1978 // Byte 1:3 – Manufacturer ID – XXYYZZh, LSB first
1979 // Byte 4 – DIMM Index, 0 base
1980 // Byte 5 – Parameter Selector
1981 // Byte 6..N – Configuration parameter data (see below for Parameters
1982 // of DIMM Information)
1983 // Response:
1984 // Byte 1 – Completion code
1985 // Byte 2..N – Configuration Parameter Data (see Table_1213h Parameters
1986 // of DIMM Information)
1987 //
1988 // Param#1 (DIMM Location):
1989 // Byte 1 – DIMM Present
1990 // Byte 1 – DIMM Present
1991 // 01h – Present
1992 // FFh – Not Present
1993 // Byte 2 – Node Number, 0 base
1994 // Byte 3 – Channel Number , 0 base
1995 // Byte 4 – DIMM Number , 0 base
1996 //
1997 // Param#2 (DIMM Type):
1998 // Byte 1 – DIMM Type
1999 // Bit [7:6]
2000 // For DDR3
2001 // 00 – Normal Voltage (1.5V)
2002 // 01 – Ultra Low Voltage (1.25V)
2003 // 10 – Low Voltage (1.35V)
2004 // 11 – Reserved
2005 // For DDR4
2006 // 00 – Reserved
2007 // 01 – Reserved
2008 // 10 – Reserved
2009 // 11 – Normal Voltage (1.2V)
2010 // Bit [5:0]
2011 // 0x00 – SDRAM
2012 // 0x01 – DDR-1 RAM
2013 // 0x02 – Rambus
2014 // 0x03 – DDR-2 RAM
2015 // 0x04 – FBDIMM
2016 // 0x05 – DDR-3 RAM
2017 // 0x06 – DDR-4 RAM
2018 //
2019 // Param#3 (DIMM Speed):
2020 // Byte 1..2 – DIMM speed in MHz, LSB
2021 // Byte 3..6 – DIMM size in Mbytes, LSB
2022 //
2023 // Param#4 (Module Part Number):
2024 // Byte 1..20 –Module Part Number (JEDEC Standard No. 21-C)
2025 //
2026 // Param#5 (Module Serial Number):
2027 // Byte 1..4 –Module Serial Number (JEDEC Standard No. 21-C)
2028 //
2029 // Param#6 (Module Manufacturer ID):
2030 // Byte 1 - Module Manufacturer ID, LSB
2031 // Byte 2 - Module Manufacturer ID, MSB
2032 //
ipmiOemQGetDimmInfo(ipmi::Context::ptr ctx,uint8_t,uint8_t,uint8_t,uint8_t dimmIndex,uint8_t paramSel)2033 ipmi::RspType<std::vector<uint8_t>> ipmiOemQGetDimmInfo(
2034 ipmi::Context::ptr ctx, uint8_t, uint8_t, uint8_t, uint8_t dimmIndex,
2035 uint8_t paramSel)
2036 {
2037 uint8_t numParam = sizeof(dimmInfoKey) / sizeof(uint8_t*);
2038 uint8_t res[MAX_BUF];
2039 std::stringstream ss;
2040 std::string str;
2041 std::string dimmType;
2042 readDimmType(dimmType, dimmIndex);
2043 auto hostId = findHost(ctx->hostIdx);
2044 if (!hostId)
2045 {
2046 phosphor::logging::log<phosphor::logging::level::ERR>(
2047 "Invalid Host Id received");
2048 return ipmi::responseInvalidCommand();
2049 }
2050 std::string dimmInfo = KEY_Q_DIMM_INFO + std::to_string(*hostId);
2051
2052 if (paramSel < 1 || paramSel >= numParam)
2053 {
2054 phosphor::logging::log<phosphor::logging::level::ERR>(
2055 "Invalid parameter received");
2056 return ipmi::responseParmOutOfRange();
2057 }
2058 ss << std::hex;
2059 ss << (int)dimmIndex;
2060 oemData[dimmInfo][ss.str()][KEY_DIMM_TYPE] = dimmType;
2061 if (oemData[dimmInfo].find(ss.str()) == oemData[dimmInfo].end())
2062 return ipmi::responseCommandNotAvailable();
2063 if (oemData[dimmInfo][ss.str()].find(dimmInfoKey[paramSel]) ==
2064 oemData[dimmInfo][ss.str()].end())
2065 return ipmi::responseCommandNotAvailable();
2066 str = oemData[dimmInfo][ss.str()][dimmInfoKey[paramSel]];
2067 int data_length = strToBytes(str, res);
2068 std::vector<uint8_t> response(&res[0], &res[data_length]);
2069 return ipmi::responseSuccess(response);
2070 }
2071
2072 //----------------------------------------------------------------------
2073 // Set Drive Info (CMD_OEM_Q_SET_DRIVE_INFO)
2074 //----------------------------------------------------------------------
2075 // BIOS issue this command to provide HDD information to BMC.
2076 //
2077 // BIOS just can get information by standard ATA / SMART command for
2078 // OB SATA controller.
2079 // BIOS can get
2080 // 1. Serial Number
2081 // 2. Model Name
2082 // 3. HDD FW Version
2083 // 4. HDD Capacity
2084 // 5. HDD WWN
2085 //
2086 // Use Get HDD info Param #5 to know the MAX HDD info index.
2087 //
2088 // Request:
2089 // Byte 1:3 – Quanta Manufacturer ID – 001C4Ch, LSB first
2090 // Byte 4 –
2091 // [7:4] Reserved
2092 // [3:0] HDD Controller Type
2093 // 0x00 – BIOS
2094 // 0x01 – Expander
2095 // 0x02 – LSI
2096 // Byte 5 – HDD Info Index, 0 base
2097 // Byte 6 – Parameter Selector
2098 // Byte 7..N – Configuration parameter data (see Table_1415h Parameters of HDD
2099 // Information)
2100 //
2101 // Response:
2102 // Byte 1 – Completion Code
2103 //
2104 // Param#0 (HDD Location):
2105 // Byte 1 – Controller
2106 // [7:3] Device Number
2107 // [2:0] Function Number
2108 // For Intel C610 series (Wellsburg)
2109 // D31:F2 (0xFA) – SATA control 1
2110 // D31:F5 (0xFD) – SATA control 2
2111 // D17:F4 (0x8C) – sSata control
2112 // Byte 2 – Port Number
2113 // Byte 3 – Location (0xFF: No HDD Present)
2114 // BIOS default set Byte 3 to 0xFF, if No HDD Present. And then skip send param
2115 // #1~4, #6, #7 to BMC (still send param #5) BIOS default set Byte 3 to 0, if
2116 // the HDD present. BMC or other people who know the HDD location has
2117 // responsibility for update Location info
2118 //
2119 // Param#1 (Serial Number):
2120 // Bytes 1..33: HDD Serial Number
2121 //
2122 // Param#2 (Model Name):
2123 // Byte 1..33 – HDD Model Name
2124 //
2125 // Param#3 (HDD FW Version):
2126 // Byte 1..17 –HDD FW version
2127 //
2128 // Param#4 (Capacity):
2129 // Byte 1..4 –HDD Block Size, LSB
2130 // Byte 5..12 - HDD Block Number, LSB
2131 // HDD Capacity = HDD Block size * HDD BLock number (Unit Byte)
2132 //
2133 // Param#5 (Max HDD Quantity):
2134 // Byte 1 - Max HDD Quantity
2135 // Max supported port numbers in this PCH
2136 //
2137 // Param#6 (HDD Type)
2138 // Byte 1 – HDD Type
2139 // 0h – Reserved
2140 // 1h – SAS
2141 // 2h – SATA
2142 // 3h – PCIE SSD (NVME)
2143 //
2144 // Param#7 (HDD WWN)
2145 // Data 1...8: HDD World Wide Name, LSB
2146 //
ipmiOemQSetDriveInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)2147 ipmi_ret_t ipmiOemQSetDriveInfo(ipmi_netfn_t, ipmi_cmd_t,
2148 ipmi_request_t request, ipmi_response_t,
2149 ipmi_data_len_t data_len, ipmi_context_t)
2150 {
2151 qDriveInfo_t* req = reinterpret_cast<qDriveInfo_t*>(request);
2152 uint8_t numParam = sizeof(driveInfoKey) / sizeof(uint8_t*);
2153 uint8_t ctrlType = req->hddCtrlType & 0x0f;
2154 std::stringstream ss;
2155 std::string str;
2156 uint8_t len = *data_len;
2157
2158 *data_len = 0;
2159
2160 /* check for requested data params */
2161 if (len < 6 || req->paramSel >= numParam || ctrlType > 2)
2162 {
2163 phosphor::logging::log<phosphor::logging::level::ERR>(
2164 "Invalid parameter received");
2165 return ipmi::ccParmOutOfRange;
2166 }
2167
2168 len = len - 6; // Get Actual data length
2169
2170 ss << std::hex;
2171 ss << std::setw(2) << std::setfill('0') << (int)req->hddIndex;
2172 oemData[KEY_Q_DRIVE_INFO][KEY_HDD_CTRL_TYPE] = req->hddCtrlType;
2173 oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()][KEY_HDD_INDEX] =
2174 req->hddIndex;
2175
2176 str = bytesToStr(req->data, len);
2177 oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()]
2178 [driveInfoKey[req->paramSel]] = str.c_str();
2179 flushOemData();
2180
2181 return ipmi::ccSuccess;
2182 }
2183
2184 //----------------------------------------------------------------------
2185 // Get Drive Info (CMD_OEM_Q_GET_DRIVE_INFO)
2186 //----------------------------------------------------------------------
2187 // BMC needs to check HDD presented or not first. If NOT presented, return
2188 // completion code 0xD5.
2189 //
2190 // Request:
2191 // Byte 1:3 – Quanta Manufacturer ID – 001C4Ch, LSB first
2192 // Byte 4 –
2193 //[7:4] Reserved
2194 //[3:0] HDD Controller Type
2195 // 0x00 – BIOS
2196 // 0x01 – Expander
2197 // 0x02 – LSI
2198 // Byte 5 – HDD Index, 0 base
2199 // Byte 6 – Parameter Selector (See Above Set HDD Information)
2200 // Response:
2201 // Byte 1 – Completion Code
2202 // 0xD5 – Not support in current status (HDD Not Present)
2203 // Byte 2..N – Configuration parameter data (see Table_1415h Parameters of HDD
2204 // Information)
2205 //
ipmiOemQGetDriveInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)2206 ipmi_ret_t ipmiOemQGetDriveInfo(
2207 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
2208 ipmi_data_len_t data_len, ipmi_context_t)
2209 {
2210 qDriveInfo_t* req = reinterpret_cast<qDriveInfo_t*>(request);
2211 uint8_t numParam = sizeof(driveInfoKey) / sizeof(uint8_t*);
2212 uint8_t* res = reinterpret_cast<uint8_t*>(response);
2213 uint8_t ctrlType = req->hddCtrlType & 0x0f;
2214 std::stringstream ss;
2215 std::string str;
2216
2217 *data_len = 0;
2218
2219 /* check for requested data params */
2220 if (req->paramSel >= numParam || ctrlType > 2)
2221 {
2222 phosphor::logging::log<phosphor::logging::level::ERR>(
2223 "Invalid parameter received");
2224 return ipmi::ccParmOutOfRange;
2225 }
2226
2227 if (oemData[KEY_Q_DRIVE_INFO].find(ctrlTypeKey[ctrlType]) ==
2228 oemData[KEY_Q_DRIVE_INFO].end())
2229 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
2230
2231 ss << std::hex;
2232 ss << std::setw(2) << std::setfill('0') << (int)req->hddIndex;
2233
2234 if (oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]].find(ss.str()) ==
2235 oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]].end())
2236 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
2237
2238 if (oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()].find(
2239 driveInfoKey[req->paramSel]) ==
2240 oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()].end())
2241 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
2242
2243 str = oemData[KEY_Q_DRIVE_INFO][ctrlTypeKey[ctrlType]][ss.str()]
2244 [driveInfoKey[req->paramSel]];
2245 *data_len = strToBytes(str, res);
2246
2247 return ipmi::ccSuccess;
2248 }
2249
2250 /* Helper function for sending DCMI commands to ME/BIC and
2251 * getting response back
2252 */
sendDCMICmd(ipmi::Context::ptr ctx,uint8_t cmd,std::vector<uint8_t> & cmdData)2253 ipmi::RspType<std::vector<uint8_t>> sendDCMICmd(
2254 [[maybe_unused]] ipmi::Context::ptr ctx, [[maybe_unused]] uint8_t cmd,
2255 std::vector<uint8_t>& cmdData)
2256 {
2257 std::vector<uint8_t> respData;
2258
2259 #if BIC_ENABLED
2260
2261 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
2262
2263 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, cmdData, respData))
2264 {
2265 return ipmi::responseUnspecifiedError();
2266 }
2267
2268 #else
2269
2270 /* Add group id as first byte to request for ME command */
2271 cmdData.insert(cmdData.begin(), groupDCMI);
2272
2273 if (sendMeCmd(ipmi::netFnGroup, cmd, cmdData, respData))
2274 {
2275 return ipmi::responseUnspecifiedError();
2276 }
2277
2278 /* Remove group id as first byte as it will be added by IPMID */
2279 respData.erase(respData.begin());
2280
2281 #endif
2282
2283 return ipmi::responseSuccess(std::move(respData));
2284 }
2285
2286 /* DCMI Command handellers. */
2287
ipmiOemDCMIGetPowerReading(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2288 ipmi::RspType<std::vector<uint8_t>> ipmiOemDCMIGetPowerReading(
2289 ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2290 {
2291 return sendDCMICmd(ctx, ipmi::dcmi::cmdGetPowerReading, reqData);
2292 }
2293
ipmiOemDCMIGetPowerLimit(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2294 ipmi::RspType<std::vector<uint8_t>> ipmiOemDCMIGetPowerLimit(
2295 ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2296 {
2297 return sendDCMICmd(ctx, ipmi::dcmi::cmdGetPowerLimit, reqData);
2298 }
2299
ipmiOemDCMISetPowerLimit(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2300 ipmi::RspType<std::vector<uint8_t>> ipmiOemDCMISetPowerLimit(
2301 ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2302 {
2303 return sendDCMICmd(ctx, ipmi::dcmi::cmdSetPowerLimit, reqData);
2304 }
2305
ipmiOemDCMIApplyPowerLimit(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2306 ipmi::RspType<std::vector<uint8_t>> ipmiOemDCMIApplyPowerLimit(
2307 ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2308 {
2309 return sendDCMICmd(ctx, ipmi::dcmi::cmdActDeactivatePwrLimit, reqData);
2310 }
2311
2312 // Https Boot related functions
ipmiOemGetHttpsData(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2313 ipmi::RspType<std::vector<uint8_t>> ipmiOemGetHttpsData(
2314 [[maybe_unused]] ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2315 {
2316 if (reqData.size() < sizeof(HttpsDataReq))
2317 return ipmi::responseReqDataLenInvalid();
2318
2319 const auto* pReq = reinterpret_cast<const HttpsDataReq*>(reqData.data());
2320 std::error_code ec;
2321 auto fileSize = std::filesystem::file_size(certPath, ec);
2322 if (ec)
2323 return ipmi::responseUnspecifiedError();
2324
2325 if (pReq->offset >= fileSize)
2326 return ipmi::responseInvalidFieldRequest();
2327
2328 std::ifstream file(certPath, std::ios::binary);
2329 if (!file)
2330 return ipmi::responseUnspecifiedError();
2331
2332 auto readLen = std::min<uint16_t>(pReq->length, fileSize - pReq->offset);
2333 std::vector<uint8_t> resData(readLen + 1);
2334 resData[0] = readLen;
2335 file.seekg(pReq->offset);
2336 file.read(reinterpret_cast<char*>(resData.data() + 1), readLen);
2337
2338 return ipmi::responseSuccess(resData);
2339 }
2340
ipmiOemGetHttpsAttr(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2341 ipmi::RspType<std::vector<uint8_t>> ipmiOemGetHttpsAttr(
2342 [[maybe_unused]] ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2343 {
2344 if (reqData.size() < sizeof(HttpsBootAttr))
2345 return ipmi::responseReqDataLenInvalid();
2346
2347 std::vector<uint8_t> resData;
2348
2349 switch (static_cast<HttpsBootAttr>(reqData[0]))
2350 {
2351 case HttpsBootAttr::certSize:
2352 {
2353 std::error_code ec;
2354 auto fileSize = std::filesystem::file_size(certPath, ec);
2355 if (ec || fileSize > std::numeric_limits<uint16_t>::max())
2356 return ipmi::responseUnspecifiedError();
2357
2358 uint16_t size = static_cast<uint16_t>(fileSize);
2359 resData.resize(sizeof(uint16_t));
2360 std::memcpy(resData.data(), &size, sizeof(uint16_t));
2361 break;
2362 }
2363 case HttpsBootAttr::certCrc:
2364 {
2365 std::ifstream file(certPath, std::ios::binary);
2366 if (!file)
2367 return ipmi::responseUnspecifiedError();
2368
2369 boost::crc_32_type result;
2370 char data[1024];
2371 while (file.read(data, sizeof(data)))
2372 result.process_bytes(data, file.gcount());
2373 if (file.gcount() > 0)
2374 result.process_bytes(data, file.gcount());
2375
2376 uint32_t crc = result.checksum();
2377 resData.resize(sizeof(uint32_t));
2378 std::memcpy(resData.data(), &crc, sizeof(uint32_t));
2379 break;
2380 }
2381 default:
2382 return ipmi::responseInvalidFieldRequest();
2383 }
2384
2385 return ipmi::responseSuccess(resData);
2386 }
2387
2388 // OEM Crashdump related functions
setDumpState(CrdState & currState,CrdState newState)2389 static ipmi_ret_t setDumpState(CrdState& currState, CrdState newState)
2390 {
2391 switch (newState)
2392 {
2393 case CrdState::waitData:
2394 if (currState == CrdState::packing)
2395 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
2396 break;
2397 case CrdState::packing:
2398 if (currState != CrdState::waitData)
2399 return CC_PARAM_NOT_SUPP_IN_CURR_STATE;
2400 break;
2401 case CrdState::free:
2402 break;
2403 default:
2404 return ipmi::ccUnspecifiedError;
2405 }
2406 currState = newState;
2407
2408 return ipmi::ccSuccess;
2409 }
2410
handleMcaBank(const CrashDumpHdr & hdr,std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2411 static ipmi_ret_t handleMcaBank(const CrashDumpHdr& hdr,
2412 std::span<const uint8_t> data,
2413 CrdState& currState, std::stringstream& ss)
2414 {
2415 if (data.size() < sizeof(CrdMcaBank))
2416 return ipmi::ccReqDataLenInvalid;
2417
2418 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2419 if (res)
2420 return res;
2421
2422 const auto* pBank = reinterpret_cast<const CrdMcaBank*>(data.data());
2423 ss << std::format(" Bank ID : 0x{:02X}, Core ID : 0x{:02X}\n",
2424 hdr.bankHdr.bankId, hdr.bankHdr.coreId);
2425 ss << std::format(" MCA_CTRL : 0x{:016X}\n", pBank->mcaCtrl);
2426 ss << std::format(" MCA_STATUS : 0x{:016X}\n", pBank->mcaSts);
2427 ss << std::format(" MCA_ADDR : 0x{:016X}\n", pBank->mcaAddr);
2428 ss << std::format(" MCA_MISC0 : 0x{:016X}\n", pBank->mcaMisc0);
2429 ss << std::format(" MCA_CTRL_MASK : 0x{:016X}\n", pBank->mcaCtrlMask);
2430 ss << std::format(" MCA_CONFIG : 0x{:016X}\n", pBank->mcaConfig);
2431 ss << std::format(" MCA_IPID : 0x{:016X}\n", pBank->mcaIpid);
2432 ss << std::format(" MCA_SYND : 0x{:016X}\n", pBank->mcaSynd);
2433 ss << std::format(" MCA_DESTAT : 0x{:016X}\n", pBank->mcaDestat);
2434 ss << std::format(" MCA_DEADDR : 0x{:016X}\n", pBank->mcaDeaddr);
2435 ss << std::format(" MCA_MISC1 : 0x{:016X}\n", pBank->mcaMisc1);
2436 ss << "\n";
2437
2438 return ipmi::ccSuccess;
2439 }
2440
2441 template <typename T>
handleVirtualBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2442 static ipmi_ret_t handleVirtualBank(std::span<const uint8_t> data,
2443 CrdState& currState, std::stringstream& ss)
2444 {
2445 if (data.size() < sizeof(T))
2446 return ipmi::ccReqDataLenInvalid;
2447
2448 const auto* pBank = reinterpret_cast<const T*>(data.data());
2449
2450 if (data.size() < sizeof(T) + sizeof(BankCorePair) * pBank->mcaCount)
2451 return ipmi::ccReqDataLenInvalid;
2452
2453 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2454 if (res)
2455 return res;
2456
2457 ss << " Virtual Bank\n";
2458 ss << std::format(" S5_RESET_STATUS : 0x{:08X}\n", pBank->s5ResetSts);
2459 ss << std::format(" PM_BREAKEVENT : 0x{:08X}\n", pBank->breakevent);
2460 if constexpr (std::is_same_v<T, CrdVirtualBankV3>)
2461 {
2462 ss << std::format(" WARMCOLDRSTSTATUS : 0x{:08X}\n", pBank->rstSts);
2463 }
2464 ss << std::format(" PROCESSOR NUMBER : 0x{:04X}\n", pBank->procNum);
2465 ss << std::format(" APIC ID : 0x{:08X}\n", pBank->apicId);
2466 ss << std::format(" EAX : 0x{:08X}\n", pBank->eax);
2467 ss << std::format(" EBX : 0x{:08X}\n", pBank->ebx);
2468 ss << std::format(" ECX : 0x{:08X}\n", pBank->ecx);
2469 ss << std::format(" EDX : 0x{:08X}\n", pBank->edx);
2470 ss << " VALID LIST : ";
2471 for (size_t i = 0; i < pBank->mcaCount; i++)
2472 {
2473 ss << std::format("(0x{:02X},0x{:02X}) ", pBank->mcaList[i].bankId,
2474 pBank->mcaList[i].coreId);
2475 }
2476 ss << "\n\n";
2477
2478 return ipmi::ccSuccess;
2479 }
2480
handleCpuWdtBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2481 static ipmi_ret_t handleCpuWdtBank(std::span<const uint8_t> data,
2482 CrdState& currState, std::stringstream& ss)
2483 {
2484 if (data.size() < sizeof(CrdCpuWdtBank))
2485 return ipmi::ccReqDataLenInvalid;
2486
2487 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2488 if (res)
2489 return res;
2490
2491 const auto* pBank = reinterpret_cast<const CrdCpuWdtBank*>(data.data());
2492 for (size_t i = 0; i < ccmNum; i++)
2493 {
2494 ss << std::format(" [CCM{}]\n", i);
2495 ss << std::format(" HwAssertStsHi : 0x{:08X}\n",
2496 pBank->hwAssertStsHi[i]);
2497 ss << std::format(" HwAssertStsLo : 0x{:08X}\n",
2498 pBank->hwAssertStsLo[i]);
2499 ss << std::format(" OrigWdtAddrLogHi : 0x{:08X}\n",
2500 pBank->origWdtAddrLogHi[i]);
2501 ss << std::format(" OrigWdtAddrLogLo : 0x{:08X}\n",
2502 pBank->origWdtAddrLogLo[i]);
2503 ss << std::format(" HwAssertMskHi : 0x{:08X}\n",
2504 pBank->hwAssertMskHi[i]);
2505 ss << std::format(" HwAssertMskLo : 0x{:08X}\n",
2506 pBank->hwAssertMskLo[i]);
2507 ss << std::format(" OrigWdtAddrLogStat : 0x{:08X}\n",
2508 pBank->origWdtAddrLogStat[i]);
2509 }
2510 ss << "\n";
2511
2512 return ipmi::ccSuccess;
2513 }
2514
2515 template <size_t N>
handleHwAssertBank(const char * name,std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2516 static ipmi_ret_t handleHwAssertBank(const char* name,
2517 std::span<const uint8_t> data,
2518 CrdState& currState, std::stringstream& ss)
2519 {
2520 if (data.size() < sizeof(CrdHwAssertBank<N>))
2521 return ipmi::ccReqDataLenInvalid;
2522
2523 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2524 if (res)
2525 return res;
2526
2527 const CrdHwAssertBank<N>* pBank =
2528 reinterpret_cast<const CrdHwAssertBank<N>*>(data.data());
2529
2530 for (size_t i = 0; i < N; i++)
2531 {
2532 ss << std::format(" [{}{}]\n", name, i);
2533 ss << std::format(" HwAssertStsHi : 0x{:08X}\n",
2534 pBank->hwAssertStsHi[i]);
2535 ss << std::format(" HwAssertStsLo : 0x{:08X}\n",
2536 pBank->hwAssertStsLo[i]);
2537 ss << std::format(" HwAssertMskHi : 0x{:08X}\n",
2538 pBank->hwAssertMskHi[i]);
2539 ss << std::format(" HwAssertMskLo : 0x{:08X}\n",
2540 pBank->hwAssertMskLo[i]);
2541 }
2542 ss << "\n";
2543
2544 return ipmi::ccSuccess;
2545 }
2546
handlePcieAerBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2547 static ipmi_ret_t handlePcieAerBank(std::span<const uint8_t> data,
2548 CrdState& currState, std::stringstream& ss)
2549 {
2550 if (data.size() < sizeof(CrdPcieAerBank))
2551 return ipmi::ccReqDataLenInvalid;
2552
2553 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2554 if (res)
2555 return res;
2556
2557 const auto* pBank = reinterpret_cast<const CrdPcieAerBank*>(data.data());
2558 ss << std::format(" [Bus{} Dev{} Fun{}]\n", pBank->bus, pBank->dev,
2559 pBank->fun);
2560 ss << std::format(" Command : 0x{:04X}\n",
2561 pBank->cmd);
2562 ss << std::format(" Status : 0x{:04X}\n",
2563 pBank->sts);
2564 ss << std::format(" Slot : 0x{:04X}\n",
2565 pBank->slot);
2566 ss << std::format(" Secondary Bus : 0x{:02X}\n",
2567 pBank->secondBus);
2568 ss << std::format(" Vendor ID : 0x{:04X}\n",
2569 pBank->vendorId);
2570 ss << std::format(" Device ID : 0x{:04X}\n",
2571 pBank->devId);
2572 ss << std::format(" Class Code : 0x{:02X}{:04X}\n",
2573 pBank->classCodeHi, pBank->classCodeLo);
2574 ss << std::format(" Bridge: Secondary Status : 0x{:04X}\n",
2575 pBank->secondSts);
2576 ss << std::format(" Bridge: Control : 0x{:04X}\n",
2577 pBank->ctrl);
2578 ss << std::format(" Uncorrectable Error Status : 0x{:08X}\n",
2579 pBank->uncorrErrSts);
2580 ss << std::format(" Uncorrectable Error Mask : 0x{:08X}\n",
2581 pBank->uncorrErrMsk);
2582 ss << std::format(" Uncorrectable Error Severity : 0x{:08X}\n",
2583 pBank->uncorrErrSeverity);
2584 ss << std::format(" Correctable Error Status : 0x{:08X}\n",
2585 pBank->corrErrSts);
2586 ss << std::format(" Correctable Error Mask : 0x{:08X}\n",
2587 pBank->corrErrMsk);
2588 ss << std::format(" Header Log DW0 : 0x{:08X}\n",
2589 pBank->hdrLogDw0);
2590 ss << std::format(" Header Log DW1 : 0x{:08X}\n",
2591 pBank->hdrLogDw1);
2592 ss << std::format(" Header Log DW2 : 0x{:08X}\n",
2593 pBank->hdrLogDw2);
2594 ss << std::format(" Header Log DW3 : 0x{:08X}\n",
2595 pBank->hdrLogDw3);
2596 ss << std::format(" Root Error Status : 0x{:08X}\n",
2597 pBank->rootErrSts);
2598 ss << std::format(" Correctable Error Source ID : 0x{:04X}\n",
2599 pBank->corrErrSrcId);
2600 ss << std::format(" Error Source ID : 0x{:04X}\n",
2601 pBank->errSrcId);
2602 ss << std::format(" Lane Error Status : 0x{:08X}\n",
2603 pBank->laneErrSts);
2604 ss << "\n";
2605
2606 return ipmi::ccSuccess;
2607 }
2608
handleWdtRegBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2609 static ipmi_ret_t handleWdtRegBank(std::span<const uint8_t> data,
2610 CrdState& currState, std::stringstream& ss)
2611 {
2612 if (data.size() < sizeof(CrdWdtRegBank))
2613 return ipmi::ccReqDataLenInvalid;
2614
2615 const auto* pBank = reinterpret_cast<const CrdWdtRegBank*>(data.data());
2616 if (data.size() < sizeof(CrdWdtRegBank) + sizeof(uint32_t) * pBank->count)
2617 return ipmi::ccReqDataLenInvalid;
2618
2619 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2620 if (res)
2621 return res;
2622
2623 ss << std::format(" [NBIO{}] {}\n", pBank->nbio, pBank->name);
2624 ss << std::format(" Address: 0x{:08X}\n", pBank->addr);
2625 ss << std::format(" Data Count: {}\n", pBank->count);
2626 ss << " Data:\n";
2627 for (size_t i = 0; i < pBank->count; i++)
2628 {
2629 ss << std::format(" {}: 0x{:08X}\n", i, pBank->data[i]);
2630 }
2631 ss << "\n";
2632
2633 return ipmi::ccSuccess;
2634 }
2635
handleCrdHdrBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2636 static ipmi_ret_t handleCrdHdrBank(std::span<const uint8_t> data,
2637 CrdState& currState, std::stringstream& ss)
2638 {
2639 if (data.size() < sizeof(CrdHdrBank))
2640 return ipmi::ccReqDataLenInvalid;
2641
2642 ipmi_ret_t res = setDumpState(currState, CrdState::waitData);
2643 if (res)
2644 return res;
2645
2646 const auto* pBank = reinterpret_cast<const CrdHdrBank*>(data.data());
2647 ss << " Crashdump Header\n";
2648 ss << std::format(" CPU PPIN : 0x{:016X}\n", pBank->ppin);
2649 ss << std::format(" UCODE VERSION : 0x{:08X}\n", pBank->ucodeVer);
2650 ss << std::format(" PMIO 80h : 0x{:08X}\n", pBank->pmio);
2651 ss << std::format(
2652 " BIT0 - SMN Parity/SMN Timeouts PSP/SMU Parity and ECC/SMN On-Package Link Error : {}\n",
2653 pBank->pmio & 0x1);
2654 ss << std::format(" BIT2 - PSP Parity and ECC : {}\n",
2655 (pBank->pmio & 0x4) >> 2);
2656 ss << std::format(" BIT3 - SMN Timeouts SMU : {}\n",
2657 (pBank->pmio & 0x8) >> 3);
2658 ss << std::format(" BIT4 - SMN Off-Package Link Packet Error : {}\n",
2659 (pBank->pmio & 0x10) >> 4);
2660 ss << "\n";
2661
2662 return ipmi::ccSuccess;
2663 }
2664
getFilename(const std::filesystem::path & dir,const std::string & prefix)2665 static std::string getFilename(const std::filesystem::path& dir,
2666 const std::string& prefix)
2667 {
2668 std::vector<int> indices;
2669 std::regex pattern(prefix + "(\\d+)\\.txt");
2670
2671 for (const auto& entry : std::filesystem::directory_iterator(dir))
2672 {
2673 std::string filename = entry.path().filename().string();
2674 std::smatch match;
2675 if (std::regex_match(filename, match, pattern))
2676 indices.push_back(std::stoi(match[1]));
2677 }
2678
2679 std::sort(indices.rbegin(), indices.rend());
2680 while (indices.size() > 2) // keep 3 files, so remove if more than 2
2681 {
2682 std::filesystem::remove(
2683 dir / (prefix + std::to_string(indices.back()) + ".txt"));
2684 indices.pop_back();
2685 }
2686
2687 int nextIndex = indices.empty() ? 1 : indices.front() + 1;
2688 return prefix + std::to_string(nextIndex) + ".txt";
2689 }
2690
handleCtrlBank(std::span<const uint8_t> data,CrdState & currState,std::stringstream & ss)2691 static ipmi_ret_t handleCtrlBank(std::span<const uint8_t> data,
2692 CrdState& currState, std::stringstream& ss)
2693 {
2694 if (data.empty())
2695 return ipmi::ccReqDataLenInvalid;
2696
2697 switch (static_cast<CrdCtrl>(data[0]))
2698 {
2699 case CrdCtrl::getState:
2700 break;
2701 case CrdCtrl::finish:
2702 {
2703 ipmi_ret_t res = setDumpState(currState, CrdState::packing);
2704 if (res)
2705 return res;
2706
2707 const std::filesystem::path dumpDir = "/var/lib/fb-ipmi-oem";
2708 std::string filename = getFilename(dumpDir, "crashdump_");
2709 std::ofstream outFile(dumpDir / filename);
2710 if (!outFile.is_open())
2711 return ipmi::ccUnspecifiedError;
2712
2713 auto now = std::chrono::system_clock::to_time_t(
2714 std::chrono::system_clock::now());
2715 outFile << "Crash Dump generated at: "
2716 << std::put_time(std::localtime(&now), "%Y-%m-%d %H:%M:%S")
2717 << "\n\n";
2718 outFile << ss.str();
2719 outFile.close();
2720 ss.str("");
2721 ss.clear();
2722 setDumpState(currState, CrdState::free);
2723 break;
2724 }
2725 default:
2726 return ccInvalidParam;
2727 }
2728
2729 return ipmi::ccSuccess;
2730 }
2731
ipmiOemCrashdump(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)2732 ipmi::RspType<std::vector<uint8_t>> ipmiOemCrashdump(
2733 [[maybe_unused]] ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
2734 {
2735 static CrdState dumpState = CrdState::free;
2736 static std::stringstream ss;
2737
2738 if (reqData.size() < sizeof(CrashDumpHdr))
2739 return ipmi::responseReqDataLenInvalid();
2740
2741 const auto* pHdr = reinterpret_cast<const CrashDumpHdr*>(reqData.data());
2742 std::span<const uint8_t> bData{reqData.data() + sizeof(CrashDumpHdr),
2743 reqData.size() - sizeof(CrashDumpHdr)};
2744 ipmi_ret_t res;
2745
2746 switch (pHdr->bankHdr.bankType)
2747 {
2748 case BankType::mca:
2749 res = handleMcaBank(*pHdr, bData, dumpState, ss);
2750 break;
2751 case BankType::virt:
2752 if (pHdr->bankHdr.version >= 3)
2753 {
2754 res = handleVirtualBank<CrdVirtualBankV3>(bData, dumpState, ss);
2755 break;
2756 }
2757 res = handleVirtualBank<CrdVirtualBankV2>(bData, dumpState, ss);
2758 break;
2759 case BankType::cpuWdt:
2760 res = handleCpuWdtBank(bData, dumpState, ss);
2761 break;
2762 case BankType::tcdx:
2763 res = handleHwAssertBank<tcdxNum>("TCDX", bData, dumpState, ss);
2764 break;
2765 case BankType::cake:
2766 res = handleHwAssertBank<cakeNum>("CAKE", bData, dumpState, ss);
2767 break;
2768 case BankType::pie0:
2769 res = handleHwAssertBank<pie0Num>("PIE", bData, dumpState, ss);
2770 break;
2771 case BankType::iom:
2772 res = handleHwAssertBank<iomNum>("IOM", bData, dumpState, ss);
2773 break;
2774 case BankType::ccix:
2775 res = handleHwAssertBank<ccixNum>("CCIX", bData, dumpState, ss);
2776 break;
2777 case BankType::cs:
2778 res = handleHwAssertBank<csNum>("CS", bData, dumpState, ss);
2779 break;
2780 case BankType::pcieAer:
2781 res = handlePcieAerBank(bData, dumpState, ss);
2782 break;
2783 case BankType::wdtReg:
2784 res = handleWdtRegBank(bData, dumpState, ss);
2785 break;
2786 case BankType::ctrl:
2787 res = handleCtrlBank(bData, dumpState, ss);
2788 if (res == ipmi::ccSuccess &&
2789 static_cast<CrdCtrl>(bData[0]) == CrdCtrl::getState)
2790 {
2791 return ipmi::responseSuccess(
2792 std::vector<uint8_t>{static_cast<uint8_t>(dumpState)});
2793 }
2794 break;
2795 case BankType::crdHdr:
2796 res = handleCrdHdrBank(bData, dumpState, ss);
2797 break;
2798 default:
2799 return ipmi::responseInvalidFieldRequest();
2800 }
2801
2802 return ipmi::response(res);
2803 }
2804
registerOEMFunctions(void)2805 static void registerOEMFunctions(void)
2806 {
2807 /* Get OEM data from json file */
2808 std::ifstream file(JSON_OEM_DATA_FILE);
2809 if (file)
2810 {
2811 try
2812 {
2813 file >> oemData;
2814 }
2815 // If parsing fails, initialize oemData as an empty JSON and
2816 // overwrite the file
2817 catch (const nlohmann::json::parse_error& e)
2818 {
2819 lg2::error("Error parsing JSON file: {ERROR}", "ERROR", e);
2820 oemData = nlohmann::json::object();
2821 std::ofstream outFile(JSON_OEM_DATA_FILE, std::ofstream::trunc);
2822 outFile << oemData.dump(4); // Write empty JSON object to the file
2823 outFile.close();
2824 }
2825 file.close();
2826 }
2827 else
2828 {
2829 lg2::info("Failed to open JSON file.");
2830 }
2831
2832 lg2::info("Registering OEM commands.");
2833
2834 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_INFO,
2835 NULL, ipmiOemDbgGetFrameInfo,
2836 PRIVILEGE_USER); // get debug frame info
2837 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ,
2838 CMD_OEM_USB_DBG_GET_UPDATED_FRAMES, NULL,
2839 ipmiOemDbgGetUpdFrames,
2840 PRIVILEGE_USER); // get debug updated frames
2841 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_POST_DESC,
2842 NULL, ipmiOemDbgGetPostDesc,
2843 PRIVILEGE_USER); // get debug post description
2844 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_GPIO_DESC,
2845 NULL, ipmiOemDbgGetGpioDesc,
2846 PRIVILEGE_USER); // get debug gpio description
2847 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_DATA,
2848 NULL, ipmiOemDbgGetFrameData,
2849 PRIVILEGE_USER); // get debug frame data
2850 ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_CTRL_PANEL,
2851 NULL, ipmiOemDbgGetCtrlPanel,
2852 PRIVILEGE_USER); // get debug control panel
2853 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_DIMM_INFO, NULL,
2854 ipmiOemSetDimmInfo,
2855 PRIVILEGE_USER); // Set Dimm Info
2856 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_GET_BOARD_ID, NULL,
2857 ipmiOemGetBoardID,
2858 PRIVILEGE_USER); // Get Board ID
2859 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemOne,
2860 CMD_OEM_GET_80PORT_RECORD, ipmi::Privilege::User,
2861 ipmiOemGet80PortRecord); // Get 80 Port Record
2862 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_MACHINE_CONFIG_INFO,
2863 NULL, ipmiOemSetMachineCfgInfo,
2864 PRIVILEGE_USER); // Set Machine Config Info
2865 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_POST_START, NULL,
2866 ipmiOemSetPostStart,
2867 PRIVILEGE_USER); // Set POST start
2868 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_POST_END, NULL,
2869 ipmiOemSetPostEnd,
2870 PRIVILEGE_USER); // Set POST End
2871 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_PPIN_INFO, NULL,
2872 ipmiOemSetPPINInfo,
2873 PRIVILEGE_USER); // Set PPIN Info
2874 #if BIC_ENABLED
2875
2876 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2877 ipmi::cmdSetSystemGuid, ipmi::Privilege::User,
2878 ipmiOemSetSystemGuid);
2879 #else
2880
2881 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_SYSTEM_GUID, NULL,
2882 ipmiOemSetSystemGuid,
2883 PRIVILEGE_USER); // Set System GUID
2884 #endif
2885 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_ADR_TRIGGER, NULL,
2886 ipmiOemSetAdrTrigger,
2887 PRIVILEGE_USER); // Set ADR Trigger
2888 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_BIOS_FLASH_INFO, NULL,
2889 ipmiOemSetBiosFlashInfo,
2890 PRIVILEGE_USER); // Set Bios Flash Info
2891 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_SET_PPR, NULL,
2892 ipmiOemSetPpr,
2893 PRIVILEGE_USER); // Set PPR
2894 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_GET_PPR, NULL,
2895 ipmiOemGetPpr,
2896 PRIVILEGE_USER); // Get PPR
2897 ipmiPrintAndRegister(ipmi::netFnOemOne, CMD_OEM_GET_FRU_ID, NULL,
2898 ipmiOemGetFruId,
2899 PRIVILEGE_USER); // Get FRU ID
2900 /* FB OEM QC Commands */
2901 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFour,
2902 CMD_OEM_Q_SET_PROC_INFO, ipmi::Privilege::User,
2903 ipmiOemQSetProcInfo); // Set Proc Info
2904 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFour,
2905 CMD_OEM_Q_GET_PROC_INFO, ipmi::Privilege::User,
2906 ipmiOemQGetProcInfo); // Get Proc Info
2907 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFour,
2908 ipmi::cmdSetQDimmInfo, ipmi::Privilege::User,
2909 ipmiOemQSetDimmInfo); // Set Dimm Info
2910 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFour,
2911 ipmi::cmdGetQDimmInfo, ipmi::Privilege::User,
2912 ipmiOemQGetDimmInfo); // Get Dimm Info
2913 ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_SET_DRIVE_INFO, NULL,
2914 ipmiOemQSetDriveInfo,
2915 PRIVILEGE_USER); // Set Drive Info
2916 ipmiPrintAndRegister(NETFUN_FB_OEM_QC, CMD_OEM_Q_GET_DRIVE_INFO, NULL,
2917 ipmiOemQGetDriveInfo,
2918 PRIVILEGE_USER); // Get Drive Info
2919
2920 /* FB OEM DCMI Commands as per DCMI spec 1.5 Section 6 */
2921 ipmi::registerGroupHandler(
2922 ipmi::prioOpenBmcBase, groupDCMI, ipmi::dcmi::cmdGetPowerReading,
2923 ipmi::Privilege::User,
2924 ipmiOemDCMIGetPowerReading); // Get Power Reading
2925
2926 ipmi::registerGroupHandler(
2927 ipmi::prioOpenBmcBase, groupDCMI, ipmi::dcmi::cmdGetPowerLimit,
2928 ipmi::Privilege::User,
2929 ipmiOemDCMIGetPowerLimit); // Get Power Limit
2930
2931 ipmi::registerGroupHandler(
2932 ipmi::prioOpenBmcBase, groupDCMI, ipmi::dcmi::cmdSetPowerLimit,
2933 ipmi::Privilege::Operator,
2934 ipmiOemDCMISetPowerLimit); // Set Power Limit
2935
2936 ipmi::registerGroupHandler(
2937 ipmi::prioOpenBmcBase, groupDCMI, ipmi::dcmi::cmdActDeactivatePwrLimit,
2938 ipmi::Privilege::Operator,
2939 ipmiOemDCMIApplyPowerLimit); // Apply Power Limit
2940
2941 /* FB OEM BOOT ORDER COMMANDS */
2942 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2943 CMD_OEM_GET_BOOT_ORDER, ipmi::Privilege::User,
2944 ipmiOemGetBootOrder); // Get Boot Order
2945
2946 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2947 CMD_OEM_SET_BOOT_ORDER, ipmi::Privilege::User,
2948 ipmiOemSetBootOrder); // Set Boot Order
2949
2950 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2951 CMD_OEM_GET_HTTPS_BOOT_DATA, ipmi::Privilege::User,
2952 ipmiOemGetHttpsData);
2953
2954 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2955 CMD_OEM_GET_HTTPS_BOOT_ATTR, ipmi::Privilege::User,
2956 ipmiOemGetHttpsAttr);
2957
2958 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2959 CMD_OEM_CRASHDUMP, ipmi::Privilege::User,
2960 ipmiOemCrashdump);
2961
2962 return;
2963 }
2964
2965 } // namespace ipmi
2966