1 #include "config.h"
2
3 #include "chassishandler.hpp"
4
5 #include <arpa/inet.h>
6 #include <endian.h>
7 #include <limits.h>
8 #include <netinet/in.h>
9
10 #include <ipmid/api.hpp>
11 #include <ipmid/types.hpp>
12 #include <ipmid/utils.hpp>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/lg2.hpp>
15 #include <sdbusplus/bus.hpp>
16 #include <sdbusplus/message/types.hpp>
17 #include <sdbusplus/server/object.hpp>
18 #include <sdbusplus/timer.hpp>
19 #include <settings.hpp>
20 #include <xyz/openbmc_project/Chassis/Buttons/Power/common.hpp>
21 #include <xyz/openbmc_project/Chassis/Buttons/Reset/common.hpp>
22 #include <xyz/openbmc_project/Chassis/Control/Power/common.hpp>
23 #include <xyz/openbmc_project/Chassis/Intrusion/client.hpp>
24 #include <xyz/openbmc_project/Common/error.hpp>
25 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
26 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
27 #include <xyz/openbmc_project/Control/Boot/Type/server.hpp>
28 #include <xyz/openbmc_project/Control/ChassisCapabilities/common.hpp>
29 #include <xyz/openbmc_project/Control/Host/NMI/common.hpp>
30 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
31 #include <xyz/openbmc_project/Led/Group/common.hpp>
32 #include <xyz/openbmc_project/Network/IP/common.hpp>
33 #include <xyz/openbmc_project/Network/MACAddress/common.hpp>
34 #include <xyz/openbmc_project/Object/Enable/common.hpp>
35 #include <xyz/openbmc_project/State/Chassis/server.hpp>
36 #include <xyz/openbmc_project/State/Host/server.hpp>
37 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
38
39 #include <array>
40 #include <chrono>
41 #include <cstring>
42 #include <filesystem>
43 #include <fstream>
44 #include <future>
45 #include <map>
46 #include <sstream>
47 #include <string>
48
49 std::unique_ptr<sdbusplus::Timer> identifyTimer
50 __attribute__((init_priority(101)));
51
52 static ChassisIDState chassisIDState = ChassisIDState::reserved;
53
54 constexpr size_t sizeVersion = 2;
55 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
56
57 // PetiBoot-Specific
58 static constexpr uint8_t netConfInitialBytes[] = {0x80, 0x21, 0x70, 0x62,
59 0x21, 0x00, 0x01, 0x06};
60 static constexpr uint8_t oemParmStart = 96;
61 static constexpr uint8_t oemParmEnd = 127;
62
63 static constexpr size_t cookieOffset = 1;
64 static constexpr size_t versionOffset = 5;
65 static constexpr size_t addrSizeOffset = 8;
66 static constexpr size_t macOffset = 9;
67 static constexpr size_t addrTypeOffset = 16;
68 static constexpr size_t ipAddrOffset = 17;
69
70 namespace ipmi
71 {
72 constexpr Cc ccParmNotSupported = 0x80;
73 constexpr Cc ccFailSetInProgress = 0x81;
74
responseParmNotSupported()75 static inline auto responseParmNotSupported()
76 {
77 return response(ccParmNotSupported);
78 }
79
responsefailSetInProgress()80 static inline auto responsefailSetInProgress()
81 {
82 return response(ccFailSetInProgress);
83 }
84 } // namespace ipmi
85
86 void registerNetFnChassisFunctions() __attribute__((constructor));
87
88 // Host settings in dbus
89 // Service name should be referenced by connection name got via object mapper
90 const char* settings_object_name = "/org/openbmc/settings/host0";
91 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
92 const char* identify_led_object_name =
93 "/xyz/openbmc_project/led/groups/enclosure_identify";
94
95 constexpr auto SETTINGS_ROOT = "/";
96 constexpr auto SETTINGS_MATCH = "host0";
97
98 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
99 static constexpr auto match = "chassis0";
100 static constexpr uint8_t chassisCapAddrMask = 0xfe;
101 static constexpr const char* powerButtonPath =
102 "/xyz/openbmc_project/Chassis/Buttons/Power0";
103 static constexpr const char* resetButtonPath =
104 "/xyz/openbmc_project/Chassis/Buttons/Reset0";
105
106 // Phosphor Host State manager
107 namespace State = sdbusplus::server::xyz::openbmc_project::state;
108 namespace fs = std::filesystem;
109
110 using namespace phosphor::logging;
111 using namespace sdbusplus::error::xyz::openbmc_project::common;
112 using namespace sdbusplus::server::xyz::openbmc_project::control::boot;
113 using Intrusion = sdbusplus::client::xyz::openbmc_project::chassis::Intrusion<>;
114 using HostState = sdbusplus::common::xyz::openbmc_project::state::Host;
115 using ChassisState = sdbusplus::common::xyz::openbmc_project::state::Chassis;
116 using NetworkIP = sdbusplus::common::xyz::openbmc_project::network::IP;
117 using MACAddress = sdbusplus::common::xyz::openbmc_project::network::MACAddress;
118 using ControlBootSource =
119 sdbusplus::common::xyz::openbmc_project::control::boot::Source;
120 using ControlBootMode =
121 sdbusplus::common::xyz::openbmc_project::control::boot::Mode;
122 using ControlBootType =
123 sdbusplus::common::xyz::openbmc_project::control::boot::Type;
124 using ObjectEnable = sdbusplus::common::xyz::openbmc_project::object::Enable;
125 using ControlPowerRestorePolicy =
126 sdbusplus::common::xyz::openbmc_project::control::power::RestorePolicy;
127 using StatePowerOnHours =
128 sdbusplus::common::xyz::openbmc_project::state::PowerOnHours;
129 using ChassisButtonsPower =
130 sdbusplus::common::xyz::openbmc_project::chassis::buttons::Power;
131 using ChassisButtonsReset =
132 sdbusplus::common::xyz::openbmc_project::chassis::buttons::Reset;
133 using ChassisControlPower =
134 sdbusplus::common::xyz::openbmc_project::chassis::control::Power;
135 using ControlChassisCapabilities =
136 sdbusplus::common::xyz::openbmc_project::control::ChassisCapabilities;
137 using ControlHostNMI =
138 sdbusplus::common::xyz::openbmc_project::control::host::NMI;
139 using LedGroup = sdbusplus::common::xyz::openbmc_project::led::Group;
140
141 namespace chassis
142 {
143 namespace internal
144 {
145
146 constexpr auto bootSettingsPath = "/xyz/openbmc_project/control/host0/boot";
147 constexpr auto bootSettingsOneTimePath =
148 "/xyz/openbmc_project/control/host0/boot/one_time";
149
150 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
151
152 namespace cache
153 {
154
155 std::unique_ptr<settings::Objects> objectsPtr = nullptr;
156
getObjects()157 settings::Objects& getObjects()
158 {
159 if (objectsPtr == nullptr)
160 {
161 objectsPtr = std::make_unique<settings::Objects>(
162 dbus, std::vector<std::string>{
163 ControlBootMode::interface, ControlBootType::interface,
164 ControlBootSource::interface,
165 ControlPowerRestorePolicy::interface});
166 }
167 return *objectsPtr;
168 }
169
170 } // namespace cache
171 } // namespace internal
172 } // namespace chassis
173
174 namespace poh
175 {
176
177 constexpr auto minutesPerCount = 60;
178
179 } // namespace poh
180
getHostNetworkData(ipmi::message::Payload & payload)181 int getHostNetworkData(ipmi::message::Payload& payload)
182 {
183 ipmi::PropertyMap properties;
184 int rc = 0;
185 uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE;
186
187 try
188 {
189 // TODO There may be cases where an interface is implemented by multiple
190 // objects,to handle such cases we are interested on that object
191 // which are on interested busname.
192 // Currenlty mapper doesn't give the readable busname(gives busid)
193 // so we can't match with bus name so giving some object specific info
194 // as SETTINGS_MATCH.
195 // Later SETTINGS_MATCH will be replaced with busname.
196
197 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
198
199 auto ipObjectInfo = ipmi::getDbusObject(bus, NetworkIP::interface,
200 SETTINGS_ROOT, SETTINGS_MATCH);
201
202 auto macObjectInfo = ipmi::getDbusObject(bus, MACAddress::interface,
203 SETTINGS_ROOT, SETTINGS_MATCH);
204
205 properties = ipmi::getAllDbusProperties(
206 bus, ipObjectInfo.second, ipObjectInfo.first, NetworkIP::interface);
207 auto variant = ipmi::getDbusProperty(
208 bus, macObjectInfo.second, macObjectInfo.first,
209 MACAddress::interface, MACAddress::property_names::mac_address);
210
211 auto ipAddress = std::get<std::string>(
212 properties[NetworkIP::property_names::address]);
213
214 auto gateway = std::get<std::string>(
215 properties[NetworkIP::property_names::gateway]);
216
217 auto prefix = std::get<uint8_t>(
218 properties[NetworkIP::property_names::prefix_length]);
219
220 uint8_t isStatic =
221 (std::get<std::string>(
222 properties[NetworkIP::property_names::origin]) ==
223 "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
224 ? 1
225 : 0;
226
227 auto MACAddress = std::get<std::string>(variant);
228
229 // it is expected here that we should get the valid data
230 // but we may also get the default values.
231 // Validation of the data is done by settings.
232 //
233 // if mac address is default mac address then
234 // don't send blank override.
235 if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS))
236 {
237 rc = -1;
238 return rc;
239 }
240 // if addr is static then ipaddress,gateway,prefix
241 // should not be default one,don't send blank override.
242 if (isStatic)
243 {
244 if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) ||
245 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix))
246 {
247 rc = -1;
248 return rc;
249 }
250 }
251
252 std::string token;
253 std::stringstream ss(MACAddress);
254
255 // First pack macOffset no of bytes in payload.
256 // Latter this PetiBoot-Specific data will be populated.
257 std::vector<uint8_t> payloadInitialBytes(macOffset);
258 payload.pack(payloadInitialBytes);
259
260 while (std::getline(ss, token, ':'))
261 {
262 payload.pack(stoi(token, nullptr, 16));
263 }
264
265 payload.pack(0x00);
266
267 payload.pack(isStatic);
268
269 uint8_t addressFamily =
270 (std::get<std::string>(
271 properties[NetworkIP::property_names::type]) ==
272 "xyz.openbmc_project.Network.IP.Protocol.IPv4")
273 ? AF_INET
274 : AF_INET6;
275
276 addrSize = (addressFamily == AF_INET)
277 ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE
278 : ipmi::network::IPV6_ADDRESS_SIZE_BYTE;
279
280 // ipaddress and gateway would be in IPv4 format
281 std::vector<uint8_t> addrInBinary(addrSize);
282 inet_pton(addressFamily, ipAddress.c_str(),
283 reinterpret_cast<void*>(addrInBinary.data()));
284
285 payload.pack(addrInBinary);
286
287 payload.pack(prefix);
288
289 std::vector<uint8_t> gatewayDetails(addrSize);
290 inet_pton(addressFamily, gateway.c_str(),
291 reinterpret_cast<void*>(gatewayDetails.data()));
292 payload.pack(gatewayDetails);
293 }
294 catch (const InternalFailure& e)
295 {
296 commit<InternalFailure>();
297 rc = -1;
298 return rc;
299 }
300
301 // PetiBoot-Specific
302 // If success then copy the first 9 bytes to the payload message
303 // payload first 2 bytes contain the parameter values. Skip that 2 bytes.
304 uint8_t skipFirstTwoBytes = 2;
305 size_t payloadSize = payload.size();
306 uint8_t* configDataStartingAddress = payload.data() + skipFirstTwoBytes;
307
308 if (payloadSize < skipFirstTwoBytes + sizeof(netConfInitialBytes))
309 {
310 lg2::error("Invalid net config");
311 rc = -1;
312 return rc;
313 }
314 std::copy(netConfInitialBytes,
315 netConfInitialBytes + sizeof(netConfInitialBytes),
316 configDataStartingAddress);
317
318 if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize))
319 {
320 lg2::error("Invalid length of address size");
321 rc = -1;
322 return rc;
323 }
324 std::copy(&addrSize, &(addrSize) + sizeof(addrSize),
325 configDataStartingAddress + addrSizeOffset);
326
327 #ifdef _IPMI_DEBUG_
328 std::printf("\n===Printing the IPMI Formatted Data========\n");
329
330 for (uint8_t pos = 0; pos < index; pos++)
331 {
332 std::printf("%02x ", payloadStartingAddress[pos]);
333 }
334 #endif
335
336 return rc;
337 }
338
339 /** @brief convert IPv4 and IPv6 addresses from binary to text form.
340 * @param[in] family - IPv4/Ipv6
341 * @param[in] data - req data pointer.
342 * @param[in] offset - offset in the data.
343 * @param[in] addrSize - size of the data which needs to be read from offset.
344 * @returns address in text form.
345 */
346
getAddrStr(uint8_t family,uint8_t * data,uint8_t offset,uint8_t addrSize)347 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset,
348 uint8_t addrSize)
349 {
350 char ipAddr[INET6_ADDRSTRLEN] = {};
351
352 switch (family)
353 {
354 case AF_INET:
355 {
356 struct sockaddr_in addr4{};
357 std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize);
358
359 inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN);
360
361 break;
362 }
363 case AF_INET6:
364 {
365 struct sockaddr_in6 addr6{};
366 std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize);
367
368 inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN);
369
370 break;
371 }
372 default:
373 {
374 return {};
375 }
376 }
377
378 return ipAddr;
379 }
380
setHostNetworkData(ipmi::message::Payload & data)381 ipmi::Cc setHostNetworkData(ipmi::message::Payload& data)
382 {
383 using namespace std::string_literals;
384 std::string hostNetworkConfig;
385 std::string mac("00:00:00:00:00:00");
386 std::string ipAddress, gateway;
387 std::string addrOrigin{0};
388 uint8_t addrSize{0};
389 std::string addressOrigin =
390 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
391 std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
392 uint8_t prefix{0};
393 uint8_t family = AF_INET;
394
395 // cookie starts from second byte
396 // version starts from sixth byte
397
398 try
399 {
400 do
401 {
402 // cookie == 0x21 0x70 0x62 0x21
403 data.trailingOk = true;
404 auto msgLen = data.size();
405 std::vector<uint8_t> msgPayloadBytes(msgLen);
406 if (data.unpack(msgPayloadBytes) != 0 || !data.fullyUnpacked())
407 {
408 lg2::error("Error in unpacking message of setHostNetworkData");
409 return ipmi::ccReqDataLenInvalid;
410 }
411
412 uint8_t* msgPayloadStartingPos = msgPayloadBytes.data();
413 constexpr size_t cookieSize = 4;
414 if (msgLen < cookieOffset + cookieSize)
415 {
416 lg2::error("Error in cookie getting of setHostNetworkData");
417 return ipmi::ccReqDataLenInvalid;
418 }
419 if (std::equal(msgPayloadStartingPos + cookieOffset,
420 msgPayloadStartingPos + cookieOffset + cookieSize,
421 (netConfInitialBytes + cookieOffset)) != 0)
422 {
423 // all cookie == 0
424 if (std::all_of(msgPayloadStartingPos + cookieOffset,
425 msgPayloadStartingPos + cookieOffset +
426 cookieSize,
427 [](int i) { return i == 0; }) == true)
428 {
429 // need to zero out the network settings.
430 break;
431 }
432
433 lg2::error("Invalid Cookie");
434 elog<InternalFailure>();
435 }
436
437 // vesion == 0x00 0x01
438 if (msgLen < versionOffset + sizeVersion)
439 {
440 lg2::error("Error in version getting of setHostNetworkData");
441 return ipmi::ccReqDataLenInvalid;
442 }
443 if (std::equal(msgPayloadStartingPos + versionOffset,
444 msgPayloadStartingPos + versionOffset + sizeVersion,
445 (netConfInitialBytes + versionOffset)) != 0)
446 {
447 lg2::error("Invalid Version");
448 elog<InternalFailure>();
449 }
450
451 if (msgLen < macOffset + 6)
452 {
453 lg2::error(
454 "Error in mac address getting of setHostNetworkData");
455 return ipmi::ccReqDataLenInvalid;
456 }
457 std::stringstream result;
458 std::copy((msgPayloadStartingPos + macOffset),
459 (msgPayloadStartingPos + macOffset + 5),
460 std::ostream_iterator<int>(result, ":"));
461 mac = result.str();
462
463 if (msgLen < addrTypeOffset + sizeof(decltype(addrOrigin)))
464 {
465 lg2::error(
466 "Error in original address getting of setHostNetworkData");
467 return ipmi::ccReqDataLenInvalid;
468 }
469 std::copy(msgPayloadStartingPos + addrTypeOffset,
470 msgPayloadStartingPos + addrTypeOffset +
471 sizeof(decltype(addrOrigin)),
472 std::ostream_iterator<int>(result, ""));
473 addrOrigin = result.str();
474
475 if (!addrOrigin.empty())
476 {
477 addressOrigin =
478 "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
479 }
480
481 if (msgLen < addrSizeOffset + sizeof(decltype(addrSize)))
482 {
483 lg2::error(
484 "Error in address size getting of setHostNetworkData");
485 return ipmi::ccReqDataLenInvalid;
486 }
487 // Get the address size
488 std::copy(msgPayloadStartingPos + addrSizeOffset,
489 (msgPayloadStartingPos + addrSizeOffset +
490 sizeof(decltype(addrSize))),
491 &addrSize);
492
493 uint8_t prefixOffset = ipAddrOffset + addrSize;
494 if (msgLen < prefixOffset + sizeof(decltype(prefix)))
495 {
496 lg2::error("Error in prefix getting of setHostNetworkData");
497 return ipmi::ccReqDataLenInvalid;
498 }
499 // std::copy(msgPayloadStartingPos + prefixOffset,
500 // msgPayloadStartingPos + prefixOffset +
501 // sizeof(decltype(prefix)),
502 // &prefix);
503 // Workaround compiler misdetecting out of bounds memcpy
504 prefix = msgPayloadStartingPos[prefixOffset];
505
506 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
507 if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE)
508 {
509 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6";
510 family = AF_INET6;
511 }
512
513 if (msgLen < ipAddrOffset + addrSize)
514 {
515 lg2::error("Error in IP address getting of setHostNetworkData");
516 return ipmi::ccReqDataLenInvalid;
517 }
518 ipAddress = getAddrStr(family, msgPayloadStartingPos, ipAddrOffset,
519 addrSize);
520
521 if (msgLen < gatewayOffset + addrSize)
522 {
523 lg2::error(
524 "Error in gateway address getting of setHostNetworkData");
525 return ipmi::ccReqDataLenInvalid;
526 }
527 gateway = getAddrStr(family, msgPayloadStartingPos, gatewayOffset,
528 addrSize);
529
530 } while (0);
531
532 // Cookie == 0 or it is a valid cookie
533 hostNetworkConfig +=
534 "ipaddress="s + ipAddress + ",prefix="s + std::to_string(prefix) +
535 ",gateway="s + gateway + ",mac="s + mac + ",addressOrigin="s +
536 addressOrigin;
537
538 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
539
540 auto ipObjectInfo = ipmi::getDbusObject(bus, NetworkIP::interface,
541 SETTINGS_ROOT, SETTINGS_MATCH);
542 auto macObjectInfo = ipmi::getDbusObject(bus, MACAddress::interface,
543 SETTINGS_ROOT, SETTINGS_MATCH);
544 // set the dbus property
545 ipmi::setDbusProperty(
546 bus, ipObjectInfo.second, ipObjectInfo.first, NetworkIP::interface,
547 NetworkIP::property_names::address, std::string(ipAddress));
548 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
549 NetworkIP::interface,
550 NetworkIP::property_names::prefix_length, prefix);
551 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
552 NetworkIP::interface,
553 NetworkIP::property_names::origin, addressOrigin);
554 ipmi::setDbusProperty(
555 bus, ipObjectInfo.second, ipObjectInfo.first, NetworkIP::interface,
556 NetworkIP::property_names::gateway, std::string(gateway));
557 ipmi::setDbusProperty(
558 bus, ipObjectInfo.second, ipObjectInfo.first, NetworkIP::interface,
559 NetworkIP::property_names::type,
560 std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4"));
561 ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first,
562 MACAddress::interface,
563 MACAddress::property_names::mac_address,
564 std::string(mac));
565
566 lg2::debug("Network configuration changed: {NETWORKCONFIG}",
567 "NETWORKCONFIG", hostNetworkConfig);
568 }
569 catch (const sdbusplus::exception_t& e)
570 {
571 commit<InternalFailure>();
572 lg2::error("Error in ipmiChassisSetSysBootOptions call");
573 return ipmi::ccUnspecifiedError;
574 }
575
576 return ipmi::ccSuccess;
577 }
578
getPOHCounter()579 uint32_t getPOHCounter()
580 {
581 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
582
583 auto chassisStateObj = ipmi::getDbusObject(
584 bus, StatePowerOnHours::interface, chassisStateRoot, match);
585
586 auto service = ipmi::getService(bus, StatePowerOnHours::interface,
587 chassisStateObj.first);
588
589 auto propValue = ipmi::getDbusProperty(
590 bus, service, chassisStateObj.first, StatePowerOnHours::interface,
591 StatePowerOnHours::property_names::poh_counter);
592
593 return std::get<uint32_t>(propValue);
594 }
595
596 /** @brief Implements the get chassis capabilities command
597 *
598 * @returns IPMI completion code plus response data
599 * chassisCapFlags - chassis capability flag
600 * chassisFRUInfoDevAddr - chassis FRU info Device Address
601 * chassisSDRDevAddr - chassis SDR device address
602 * chassisSELDevAddr - chassis SEL device address
603 * chassisSMDevAddr - chassis system management device address
604 * chassisBridgeDevAddr - chassis bridge device address
605 */
606 ipmi::RspType<bool, // chassis intrusion sensor
607 bool, // chassis Front panel lockout
608 bool, // chassis NMI
609 bool, // chassis power interlock
610 uint4_t, // reserved
611 uint8_t, // chassis FRU info Device Address
612 uint8_t, // chassis SDR device address
613 uint8_t, // chassis SEL device address
614 uint8_t, // chassis system management device address
615 uint8_t // chassis bridge device address
616 >
ipmiGetChassisCap()617 ipmiGetChassisCap()
618 {
619 ipmi::PropertyMap properties;
620 try
621 {
622 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
623
624 ipmi::DbusObjectInfo chassisCapObject =
625 ipmi::getDbusObject(bus, ControlChassisCapabilities::interface);
626
627 // capabilities flags
628 // [7..4] - reserved
629 // [3] – 1b = provides power interlock (IPM 1.5)
630 // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
631 // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
632 // has capabilities
633 // to lock out external power control and reset button or
634 // front panel interfaces and/or detect tampering with those
635 // interfaces).
636 // [0] -1b = Chassis provides intrusion (physical security) sensor.
637 // set to default value 0x0.
638
639 properties = ipmi::getAllDbusProperties(
640 bus, chassisCapObject.second, chassisCapObject.first,
641 ControlChassisCapabilities::interface);
642 }
643 catch (const std::exception& e)
644 {
645 lg2::error("Failed to fetch Chassis Capability properties: {ERROR}",
646 "ERROR", e);
647 return ipmi::responseUnspecifiedError();
648 }
649
650 bool* chassisIntrusionFlag = std::get_if<bool>(
651 &properties[ControlChassisCapabilities::property_names::
652 chassis_intrusion_enabled]);
653 if (chassisIntrusionFlag == nullptr)
654 {
655 lg2::error("Error to get chassis Intrusion flags");
656 return ipmi::responseUnspecifiedError();
657 }
658 bool* chassisFrontPanelFlag = std::get_if<bool>(
659 &properties[ControlChassisCapabilities::property_names::
660 chassis_front_panel_lockout_enabled]);
661 if (chassisFrontPanelFlag == nullptr)
662 {
663 lg2::error("Error to get chassis intrusion flags");
664 return ipmi::responseUnspecifiedError();
665 }
666 bool* chassisNMIFlag = std::get_if<bool>(
667 &properties
668 [ControlChassisCapabilities::property_names::chassis_nmi_enabled]);
669 if (chassisNMIFlag == nullptr)
670 {
671 lg2::error("Error to get chassis NMI flags");
672 return ipmi::responseUnspecifiedError();
673 }
674 bool* chassisPowerInterlockFlag = std::get_if<bool>(
675 &properties[ControlChassisCapabilities::property_names::
676 chassis_power_interlock_enabled]);
677 if (chassisPowerInterlockFlag == nullptr)
678 {
679 lg2::error("Error to get chassis power interlock flags");
680 return ipmi::responseUnspecifiedError();
681 }
682 uint8_t* chassisFRUInfoDevAddr = std::get_if<uint8_t>(
683 &properties
684 [ControlChassisCapabilities::property_names::fru_device_address]);
685 if (chassisFRUInfoDevAddr == nullptr)
686 {
687 lg2::error("Error to get chassis FRU info device address");
688 return ipmi::responseUnspecifiedError();
689 }
690 uint8_t* chassisSDRDevAddr = std::get_if<uint8_t>(
691 &properties
692 [ControlChassisCapabilities::property_names::sdr_device_address]);
693 if (chassisSDRDevAddr == nullptr)
694 {
695 lg2::error("Error to get chassis SDR device address");
696 return ipmi::responseUnspecifiedError();
697 }
698 uint8_t* chassisSELDevAddr = std::get_if<uint8_t>(
699 &properties
700 [ControlChassisCapabilities::property_names::sel_device_address]);
701 if (chassisSELDevAddr == nullptr)
702 {
703 lg2::error("Error to get chassis SEL device address");
704 return ipmi::responseUnspecifiedError();
705 }
706 uint8_t* chassisSMDevAddr = std::get_if<uint8_t>(
707 &properties
708 [ControlChassisCapabilities::property_names::sm_device_address]);
709 if (chassisSMDevAddr == nullptr)
710 {
711 lg2::error("Error to get chassis SM device address");
712 return ipmi::responseUnspecifiedError();
713 }
714 uint8_t* chassisBridgeDevAddr = std::get_if<uint8_t>(
715 &properties[ControlChassisCapabilities::property_names::
716 bridge_device_address]);
717 if (chassisBridgeDevAddr == nullptr)
718 {
719 lg2::error("Error to get chassis bridge device address");
720 return ipmi::responseUnspecifiedError();
721 }
722
723 return ipmi::responseSuccess(
724 *chassisIntrusionFlag, *chassisFrontPanelFlag, *chassisNMIFlag,
725 *chassisPowerInterlockFlag, 0, *chassisFRUInfoDevAddr,
726 *chassisSDRDevAddr, *chassisSELDevAddr, *chassisSMDevAddr,
727 *chassisBridgeDevAddr);
728 }
729
730 /** @brief implements set chassis capalibities command
731 * @param intrusion - chassis intrusion
732 * @param fpLockout - frontpannel lockout
733 * @param reserved1 - skip one bit
734 * @param fruDeviceAddr - chassis FRU info Device Address
735 * @param sdrDeviceAddr - chassis SDR device address
736 * @param selDeviceAddr - chassis SEL device address
737 * @param smDeviceAddr - chassis system management device address
738 * @param bridgeDeviceAddr - chassis bridge device address
739 *
740 * @returns IPMI completion code
741 */
ipmiSetChassisCap(bool intrusion,bool fpLockout,uint6_t reserved1,uint8_t fruDeviceAddr,uint8_t sdrDeviceAddr,uint8_t selDeviceAddr,uint8_t smDeviceAddr,std::optional<uint8_t> bridgeDeviceAddr)742 ipmi::RspType<> ipmiSetChassisCap(
743 bool intrusion, bool fpLockout, uint6_t reserved1,
744
745 uint8_t fruDeviceAddr,
746
747 uint8_t sdrDeviceAddr,
748
749 uint8_t selDeviceAddr,
750
751 uint8_t smDeviceAddr,
752
753 std::optional<uint8_t> bridgeDeviceAddr)
754 {
755 // check input data
756 if (reserved1 != 0)
757 {
758 lg2::error("Unsupported request parameter");
759 return ipmi::responseInvalidFieldRequest();
760 }
761
762 if ((fruDeviceAddr & ~chassisCapAddrMask) != 0)
763 {
764 lg2::error("Unsupported request parameter(FRU Addr) for REQ={REQ}",
765 "REQ", lg2::hex, fruDeviceAddr);
766 return ipmi::responseInvalidFieldRequest();
767 }
768 if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0)
769 {
770 lg2::error("Unsupported request parameter(SDR Addr) for REQ={REQ}",
771 "REQ", lg2::hex, sdrDeviceAddr);
772 return ipmi::responseInvalidFieldRequest();
773 }
774
775 if ((selDeviceAddr & ~chassisCapAddrMask) != 0)
776 {
777 lg2::error("Unsupported request parameter(SEL Addr) for REQ={REQ}",
778 "REQ", lg2::hex, selDeviceAddr);
779 return ipmi::responseInvalidFieldRequest();
780 }
781
782 if ((smDeviceAddr & ~chassisCapAddrMask) != 0)
783 {
784 lg2::error("Unsupported request parameter(SM Addr) for REQ={REQ}",
785 "REQ", lg2::hex, smDeviceAddr);
786 return ipmi::responseInvalidFieldRequest();
787 }
788
789 if (bridgeDeviceAddr.has_value())
790 {
791 if ((bridgeDeviceAddr.value() & ~chassisCapAddrMask) != 0)
792 {
793 lg2::error(
794 "Unsupported request parameter(Bridge Addr) for REQ={REQ}",
795 "REQ", lg2::hex, bridgeDeviceAddr.value());
796 return ipmi::responseInvalidFieldRequest();
797 }
798 }
799
800 try
801 {
802 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
803 ipmi::DbusObjectInfo chassisCapObject =
804 ipmi::getDbusObject(bus, ControlChassisCapabilities::interface);
805
806 ipmi::setDbusProperty(
807 bus, chassisCapObject.second, chassisCapObject.first,
808 ControlChassisCapabilities::interface,
809 ControlChassisCapabilities::property_names::
810 chassis_intrusion_enabled,
811 intrusion);
812
813 ipmi::setDbusProperty(
814 bus, chassisCapObject.second, chassisCapObject.first,
815 ControlChassisCapabilities::interface,
816 ControlChassisCapabilities::property_names::
817 chassis_front_panel_lockout_enabled,
818 fpLockout);
819
820 ipmi::setDbusProperty(
821 bus, chassisCapObject.second, chassisCapObject.first,
822 ControlChassisCapabilities::interface,
823 ControlChassisCapabilities::property_names::fru_device_address,
824 fruDeviceAddr);
825
826 ipmi::setDbusProperty(
827 bus, chassisCapObject.second, chassisCapObject.first,
828 ControlChassisCapabilities::interface,
829 ControlChassisCapabilities::property_names::sdr_device_address,
830 sdrDeviceAddr);
831
832 ipmi::setDbusProperty(
833 bus, chassisCapObject.second, chassisCapObject.first,
834 ControlChassisCapabilities::interface,
835 ControlChassisCapabilities::property_names::sel_device_address,
836 selDeviceAddr);
837
838 ipmi::setDbusProperty(
839 bus, chassisCapObject.second, chassisCapObject.first,
840 ControlChassisCapabilities::interface,
841 ControlChassisCapabilities::property_names::sm_device_address,
842 smDeviceAddr);
843
844 if (bridgeDeviceAddr.has_value())
845 {
846 ipmi::setDbusProperty(
847 bus, chassisCapObject.second, chassisCapObject.first,
848 ControlChassisCapabilities::interface,
849 ControlChassisCapabilities::property_names::
850 bridge_device_address,
851 bridgeDeviceAddr.value());
852 }
853 }
854 catch (const std::exception& e)
855 {
856 lg2::error("Failed to set chassis capability properties: {ERR}", "ERR",
857 e);
858 return ipmi::responseUnspecifiedError();
859 }
860 return ipmi::responseSuccess();
861 }
862
863 //------------------------------------------
864 // Calls into Host State Manager Dbus object
865 //------------------------------------------
initiateHostStateTransition(ipmi::Context::ptr & ctx,State::Host::Transition transition)866 int initiateHostStateTransition(ipmi::Context::ptr& ctx,
867 State::Host::Transition transition)
868 {
869 // OpenBMC Host State Manager dbus framework
870 const auto hostStatePath =
871 std::format("{}/{}", HostState::namespace_path::value, "host0");
872
873 // Convert to string equivalent of the passed in transition enum.
874 auto request =
875 sdbusplus::common::xyz::openbmc_project::state::convertForMessage(
876 transition);
877
878 std::string service;
879 boost::system::error_code ec =
880 ipmi::getService(ctx, HostState::interface, hostStatePath, service);
881
882 if (!ec)
883 {
884 ec = ipmi::setDbusProperty(
885 ctx, service, hostStatePath, HostState::interface,
886 HostState::property_names::requested_host_transition, request);
887 }
888 if (ec)
889 {
890 lg2::error(
891 "Failed to initiate transition for request {REQUEST}: {EXCEPTION}",
892 "REQUEST", request, "EXCEPTION", ec.message());
893 return -1;
894 }
895 lg2::info(
896 "Transition request {REQUEST} initiated successfully by user {USERID}",
897 "REQUEST", request, "USERID", ctx->userId);
898 return 0;
899 }
900
901 //------------------------------------------
902 // Calls into Chassis State Manager Dbus object
903 //------------------------------------------
initiateChassisStateTransition(ipmi::Context::ptr & ctx,State::Chassis::Transition transition)904 int initiateChassisStateTransition(ipmi::Context::ptr& ctx,
905 State::Chassis::Transition transition)
906 {
907 // OpenBMC Chassis State Manager dbus framework
908 constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0";
909
910 std::string service;
911 boost::system::error_code ec = ipmi::getService(
912 ctx, ChassisState::interface, chassisStatePath, service);
913
914 // Convert to string equivalent of the passed in transition enum.
915 auto request =
916 sdbusplus::common::xyz::openbmc_project::state::convertForMessage(
917 transition);
918
919 if (!ec)
920 {
921 ec = ipmi::setDbusProperty(
922 ctx, service, chassisStatePath, ChassisState::interface,
923 ChassisState::property_names::requested_power_transition, request);
924 }
925 if (ec)
926 {
927 lg2::error("Failed to initiate transition {REQUEST}: {EXCEPTION}",
928 "REQUEST", request, "EXCEPTION", ec.message());
929
930 return -1;
931 }
932
933 return 0;
934 }
935
936 //------------------------------------------
937 // Trigger an NMI on the host via dbus
938 //------------------------------------------
doNmi(ipmi::Context::ptr & ctx)939 static int doNmi(ipmi::Context::ptr& ctx)
940 {
941 ipmi::DbusObjectInfo nmiObj{};
942 boost::system::error_code ec;
943
944 ec = ipmi::getDbusObject(ctx, ControlHostNMI::interface, nmiObj);
945 if (ec)
946 {
947 lg2::error("Failed to find NMI service: {ERROR}", "ERROR",
948 ec.message());
949 return -1;
950 }
951
952 ec = ipmi::callDbusMethod(ctx, nmiObj.second, nmiObj.first,
953 ControlHostNMI::interface, "NMI");
954 if (ec)
955 {
956 lg2::error("NMI call failed: {ERROR}", "ERROR", ec.message());
957 elog<InternalFailure>();
958 return -1;
959 }
960
961 return 0;
962 }
963
964 namespace power_policy
965 {
966
967 using namespace sdbusplus::server::xyz::openbmc_project::control::power;
968 using IpmiValue = uint8_t;
969 using DbusValue = RestorePolicy::Policy;
970
971 const std::map<DbusValue, IpmiValue> dbusToIpmi = {
972 {RestorePolicy::Policy::AlwaysOff, 0x00},
973 {RestorePolicy::Policy::Restore, 0x01},
974 {RestorePolicy::Policy::AlwaysOn, 0x02},
975 {RestorePolicy::Policy::None, 0x03}};
976
977 static constexpr uint8_t noChange = 0x03;
978 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
979
980 /* helper function for Get Chassis Status Command
981 */
getPowerRestorePolicy()982 std::optional<uint2_t> getPowerRestorePolicy()
983 {
984 uint2_t restorePolicy = 0;
985 using namespace chassis::internal;
986
987 settings::Objects& objects = cache::getObjects();
988
989 try
990 {
991 const auto& powerRestoreSetting =
992 objects.map.at(ControlPowerRestorePolicy::interface).front();
993 const auto& powerRestoreSettingService =
994 objects.serviceMap.at(powerRestoreSetting);
995 ipmi::Value result = ipmi::getDbusProperty(
996 *getSdBus(), powerRestoreSettingService, powerRestoreSetting,
997 ControlPowerRestorePolicy::interface,
998 ControlPowerRestorePolicy::property_names::power_restore_policy);
999 auto powerRestore = RestorePolicy::convertPolicyFromString(
1000 std::get<std::string>(result));
1001 restorePolicy = dbusToIpmi.at(powerRestore);
1002 }
1003 catch (const std::exception& e)
1004 {
1005 lg2::error(
1006 "Failed to fetch pgood property ({PATH}/{INTERFACE}): {ERROR}",
1007 "PATH",
1008 objects.map.at(ControlPowerRestorePolicy::interface).front(),
1009 "INTERFACE", ControlPowerRestorePolicy::interface, "ERROR", e);
1010 cache::objectsPtr.reset();
1011 return std::nullopt;
1012 }
1013 return std::make_optional(restorePolicy);
1014 }
1015
1016 /*
1017 * getPowerStatus
1018 * helper function for Get Chassis Status Command
1019 * return - optional value for pgood (no value on error)
1020 */
getPowerStatus()1021 std::optional<bool> getPowerStatus()
1022 {
1023 bool powerGood = false;
1024 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1025 try
1026 {
1027 constexpr const char* chassisStatePath =
1028 "/xyz/openbmc_project/state/chassis0";
1029 auto service =
1030 ipmi::getService(*busp, ChassisState::interface, chassisStatePath);
1031
1032 ipmi::Value powerState = ipmi::getDbusProperty(
1033 *busp, service, chassisStatePath, ChassisState::interface,
1034 ChassisState::property_names::current_power_state);
1035 std::string powerStateStr = std::get<std::string>(powerState);
1036 if (powerStateStr.ends_with(".On") ||
1037 powerStateStr.ends_with(".TransitioningToOff"))
1038 {
1039 powerGood = true;
1040 }
1041 }
1042 catch (const std::exception& e)
1043 {
1044 try
1045 {
1046 // FIXME: some legacy modules use the older path; try that next
1047 constexpr const char* legacyPwrCtrlObj =
1048 "/org/openbmc/control/power0";
1049 constexpr const char* legacyPwrCtrlIntf =
1050 "org.openbmc.control.Power";
1051 auto service =
1052 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
1053
1054 ipmi::Value variant = ipmi::getDbusProperty(
1055 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
1056 powerGood = static_cast<bool>(std::get<int>(variant));
1057 }
1058 catch (const std::exception& e)
1059 {
1060 lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e);
1061 return std::nullopt;
1062 }
1063 }
1064 return std::make_optional(powerGood);
1065 }
1066
1067 /*
1068 * getACFailStatus
1069 * helper function for Get Chassis Status Command
1070 * return - bool value for ACFail (false on error)
1071 */
getACFailStatus()1072 bool getACFailStatus()
1073 {
1074 constexpr const char* powerControlObj =
1075 "/xyz/openbmc_project/Chassis/Control/Power0";
1076 bool acFail = false;
1077 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1078 try
1079 {
1080 auto service = ipmi::getService(*bus, ChassisControlPower::interface,
1081 powerControlObj);
1082
1083 ipmi::Value variant =
1084 ipmi::getDbusProperty(*bus, service, powerControlObj,
1085 ChassisControlPower::interface, "PFail");
1086 acFail = std::get<bool>(variant);
1087 }
1088 catch (const std::exception& e)
1089 {
1090 lg2::error(
1091 "Failed to fetch PFail property ({PATH}/{INTERFACE}): {ERROR}",
1092 "PATH", powerControlObj, "INTERFACE",
1093 ChassisControlPower::interface, "ERROR", e);
1094 }
1095 return acFail;
1096 }
1097 } // namespace power_policy
1098
getButtonDisabled(ipmi::Context::ptr & ctx,const std::string & buttonPath,const std::string & buttonIntf)1099 static std::optional<bool> getButtonDisabled(ipmi::Context::ptr& ctx,
1100 const std::string& buttonPath,
1101 const std::string& buttonIntf)
1102 {
1103 bool buttonDisabled = false;
1104 boost::system::error_code ec;
1105 std::string service;
1106 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1107 if (!ec)
1108 {
1109 bool enabled;
1110 ec = ipmi::getDbusProperty(ctx, service, buttonPath, buttonIntf,
1111 "Enabled", enabled);
1112 buttonDisabled = !enabled;
1113 }
1114
1115 if (ec)
1116 {
1117 lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}",
1118 "PATH", buttonPath, "ERROR", ec.message());
1119 return std::nullopt;
1120 }
1121 return std::make_optional(buttonDisabled);
1122 }
1123
setButtonDisabled(ipmi::Context::ptr & ctx,const std::string & buttonPath,const std::string & buttonIntf,bool disable)1124 static bool setButtonDisabled(ipmi::Context::ptr& ctx,
1125 const std::string& buttonPath,
1126 const std::string& buttonIntf, bool disable)
1127 {
1128 std::string service;
1129 boost::system::error_code ec;
1130 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1131 if (!ec)
1132 {
1133 ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf,
1134 "Enabled", !disable);
1135 }
1136 if (ec)
1137 {
1138 lg2::error(
1139 "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}",
1140 "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message());
1141 return false;
1142 }
1143 return true;
1144 }
1145
getChassisIntrusionStatus(ipmi::Context::ptr & ctx)1146 static std::optional<bool> getChassisIntrusionStatus(ipmi::Context::ptr& ctx)
1147 {
1148 std::vector<std::string> interfaces = {std::string(Intrusion::interface)};
1149 ipmi::ObjectTree objs;
1150 std::string propVal;
1151 std::optional<bool> ret = std::nullopt;
1152
1153 boost::system::error_code ec =
1154 ipmi::getSubTree(ctx, interfaces, std::string("/"), 0, objs);
1155
1156 if (ec)
1157 {
1158 lg2::error("Fail to find Chassis Intrusion Interface on D-Bus "
1159 "({INTERFACE}): {ERROR}",
1160 "INTERFACE", Intrusion::interface, "ERROR", ec.message());
1161 return ret;
1162 }
1163
1164 for (const auto& [path, map] : objs)
1165 {
1166 for (const auto& [service, intfs] : map)
1167 {
1168 ec = ipmi::getDbusProperty<std::string>(
1169 ctx, service, path, Intrusion::interface,
1170 Intrusion::property_names::status, propVal);
1171
1172 if (ec)
1173 {
1174 lg2::error("Failed to get Chassis Intrusion Status property "
1175 "({SERVICE}/{PATH}/{INTERFACE}): {ERROR}",
1176 "SERVICE", service, "PATH", path, "INTERFACE",
1177 Intrusion::interface, "ERROR", ec.message());
1178 continue;
1179 }
1180
1181 auto statusOpt =
1182 sdbusplus::message::convert_from_string<Intrusion::Status>(
1183 propVal);
1184 if (statusOpt)
1185 {
1186 if (*statusOpt == Intrusion::Status::Normal)
1187 {
1188 ret = std::make_optional(false);
1189 }
1190 else
1191 {
1192 ret = std::make_optional(true);
1193 return ret; // Early return on first non-Normal status
1194 }
1195 }
1196 else
1197 {
1198 lg2::warning(
1199 "Invalid Intrusion::Status value received: {VALUE}",
1200 "VALUE", propVal);
1201 return std::nullopt;
1202 }
1203 }
1204 }
1205 return ret;
1206 }
1207
1208 //----------------------------------------------------------------------
1209 // Get Chassis Status commands
1210 //----------------------------------------------------------------------
1211 ipmi::RspType<bool, // Power is on
1212 bool, // Power overload
1213 bool, // Interlock
1214 bool, // power fault
1215 bool, // power control fault
1216 uint2_t, // power restore policy
1217 bool, // reserved
1218
1219 bool, // AC failed
1220 bool, // last power down caused by a Power overload
1221 bool, // last power down caused by a power interlock
1222 bool, // last power down caused by power fault
1223 bool, // last ‘Power is on’ state was entered via IPMI command
1224 uint3_t, // reserved
1225
1226 bool, // Chassis intrusion active
1227 bool, // Front Panel Lockout active
1228 bool, // Drive Fault
1229 bool, // Cooling/fan fault detected
1230 uint2_t, // Chassis Identify State
1231 bool, // Chassis Identify command and state info supported
1232 bool, // reserved
1233
1234 bool, // Power off button disabled
1235 bool, // Reset button disabled
1236 bool, // Diagnostic Interrupt button disabled
1237 bool, // Standby (sleep) button disabled
1238 bool, // Power off button disable allowed
1239 bool, // Reset button disable allowed
1240 bool, // Diagnostic Interrupt button disable allowed
1241 bool // Standby (sleep) button disable allowed
1242 >
ipmiGetChassisStatus(ipmi::Context::ptr & ctx)1243 ipmiGetChassisStatus(ipmi::Context::ptr& ctx)
1244 {
1245 using namespace chassis::internal;
1246 std::optional<uint2_t> restorePolicy =
1247 power_policy::getPowerRestorePolicy();
1248 std::optional<bool> powerGood = power_policy::getPowerStatus();
1249 if (!restorePolicy || !powerGood)
1250 {
1251 return ipmi::responseUnspecifiedError();
1252 }
1253
1254 // Front Panel Button Capabilities and disable/enable status(Optional)
1255 std::optional<bool> powerButtonReading =
1256 getButtonDisabled(ctx, powerButtonPath, ChassisButtonsPower::interface);
1257 // allow disable if the interface is present
1258 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
1259 // default return the button is enabled (not disabled)
1260 bool powerButtonDisabled = false;
1261 if (powerButtonDisableAllow)
1262 {
1263 // return the real value of the button status, if present
1264 powerButtonDisabled = *powerButtonReading;
1265 }
1266
1267 std::optional<bool> resetButtonReading =
1268 getButtonDisabled(ctx, resetButtonPath, ChassisButtonsReset::interface);
1269 // allow disable if the interface is present
1270 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
1271 // default return the button is enabled (not disabled)
1272 bool resetButtonDisabled = false;
1273 if (resetButtonDisableAllow)
1274 {
1275 // return the real value of the button status, if present
1276 resetButtonDisabled = *resetButtonReading;
1277 }
1278
1279 bool powerDownAcFailed = power_policy::getACFailStatus();
1280
1281 bool chassisIntrusionActive = false;
1282 std::optional<bool> chassisIntrusionStatus = getChassisIntrusionStatus(ctx);
1283 if (chassisIntrusionStatus)
1284 {
1285 chassisIntrusionActive = chassisIntrusionStatus.value();
1286 }
1287
1288 // This response has a lot of hard-coded, unsupported fields
1289 // They are set to false or 0
1290 constexpr bool powerOverload = false;
1291 constexpr bool chassisInterlock = false;
1292 constexpr bool powerFault = false;
1293 constexpr bool powerControlFault = false;
1294 constexpr bool powerDownOverload = false;
1295 constexpr bool powerDownInterlock = false;
1296 constexpr bool powerDownPowerFault = false;
1297 constexpr bool powerStatusIPMI = false;
1298 constexpr bool frontPanelLockoutActive = false;
1299 constexpr bool driveFault = false;
1300 constexpr bool coolingFanFault = false;
1301 // chassisIdentifySupport set because this command is implemented
1302 constexpr bool chassisIdentifySupport = true;
1303 uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
1304 constexpr bool diagButtonDisabled = false;
1305 constexpr bool sleepButtonDisabled = false;
1306 constexpr bool diagButtonDisableAllow = false;
1307 constexpr bool sleepButtonDisableAllow = false;
1308
1309 return ipmi::responseSuccess(
1310 *powerGood, powerOverload, chassisInterlock, powerFault,
1311 powerControlFault, *restorePolicy,
1312 false, // reserved
1313
1314 powerDownAcFailed, powerDownOverload, powerDownInterlock,
1315 powerDownPowerFault, powerStatusIPMI,
1316 uint3_t(0), // reserved
1317
1318 chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1319 coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1320 false, // reserved
1321
1322 powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1323 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1324 diagButtonDisableAllow, sleepButtonDisableAllow);
1325 }
1326
1327 enum class IpmiRestartCause
1328 {
1329 Unknown = 0x0,
1330 RemoteCommand = 0x1,
1331 ResetButton = 0x2,
1332 PowerButton = 0x3,
1333 WatchdogTimer = 0x4,
1334 PowerPolicyAlwaysOn = 0x6,
1335 PowerPolicyPreviousState = 0x7,
1336 SoftReset = 0xa,
1337 };
1338
restartCauseToIpmiRestartCause(State::Host::RestartCause cause)1339 static IpmiRestartCause restartCauseToIpmiRestartCause(
1340 State::Host::RestartCause cause)
1341 {
1342 switch (cause)
1343 {
1344 case State::Host::RestartCause::Unknown:
1345 {
1346 return IpmiRestartCause::Unknown;
1347 }
1348 case State::Host::RestartCause::RemoteCommand:
1349 {
1350 return IpmiRestartCause::RemoteCommand;
1351 }
1352 case State::Host::RestartCause::ResetButton:
1353 {
1354 return IpmiRestartCause::ResetButton;
1355 }
1356 case State::Host::RestartCause::PowerButton:
1357 {
1358 return IpmiRestartCause::PowerButton;
1359 }
1360 case State::Host::RestartCause::WatchdogTimer:
1361 {
1362 return IpmiRestartCause::WatchdogTimer;
1363 }
1364 case State::Host::RestartCause::PowerPolicyAlwaysOn:
1365 {
1366 return IpmiRestartCause::PowerPolicyAlwaysOn;
1367 }
1368 case State::Host::RestartCause::PowerPolicyPreviousState:
1369 {
1370 return IpmiRestartCause::PowerPolicyPreviousState;
1371 }
1372 case State::Host::RestartCause::SoftReset:
1373 {
1374 return IpmiRestartCause::SoftReset;
1375 }
1376 default:
1377 {
1378 return IpmiRestartCause::Unknown;
1379 }
1380 }
1381 }
1382
1383 /*
1384 * getRestartCause
1385 * helper function for Get Host restart cause Command
1386 * return - optional value for RestartCause (no value on error)
1387 */
getRestartCause(ipmi::Context::ptr ctx)1388 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx)
1389 {
1390 constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0";
1391
1392 std::string service;
1393 boost::system::error_code ec =
1394 ipmi::getService(ctx, HostState::interface, restartCausePath, service);
1395 if (!ec)
1396 {
1397 std::string restartCauseStr;
1398 ec = ipmi::getDbusProperty<std::string>(
1399 ctx, service, restartCausePath, HostState::interface,
1400 HostState::property_names::restart_cause, restartCauseStr);
1401 if (!ec)
1402 {
1403 auto cause =
1404 State::Host::convertRestartCauseFromString(restartCauseStr);
1405 return types::enum_cast<uint4_t>(
1406 restartCauseToIpmiRestartCause(cause));
1407 }
1408 }
1409
1410 lg2::error(
1411 "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}",
1412 "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE",
1413 HostState::interface);
1414 return std::nullopt;
1415 }
1416
1417 ipmi::RspType<uint4_t, // Restart Cause
1418 uint4_t, // reserved
1419 uint8_t // channel number (not supported)
1420 >
ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)1421 ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
1422 {
1423 std::optional<uint4_t> cause = getRestartCause(ctx);
1424 if (!cause)
1425 {
1426 return ipmi::responseUnspecifiedError();
1427 }
1428
1429 constexpr uint4_t reserved = 0;
1430 auto channel = static_cast<uint8_t>(ctx->channel);
1431 return ipmi::responseSuccess(cause.value(), reserved, channel);
1432 }
1433 /** @brief Implementation of chassis control command
1434 *
1435 * @param - chassisControl command byte
1436 *
1437 * @return Success or InvalidFieldRequest.
1438 */
ipmiChassisControl(ipmi::Context::ptr & ctx,uint8_t chassisControl)1439 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
1440 uint8_t chassisControl)
1441 {
1442 int rc = 0;
1443 switch (chassisControl)
1444 {
1445 case cmdPowerOn:
1446 rc = initiateHostStateTransition(ctx, State::Host::Transition::On);
1447 break;
1448 case cmdPowerOff:
1449 rc = initiateChassisStateTransition(
1450 ctx, State::Chassis::Transition::Off);
1451 break;
1452 case cmdHardReset:
1453 rc = initiateHostStateTransition(
1454 ctx, State::Host::Transition::ForceWarmReboot);
1455 break;
1456 case cmdPowerCycle:
1457 {
1458 auto powerState = power_policy::getPowerStatus();
1459
1460 if (powerState == std::nullopt)
1461 {
1462 return ipmi::responseUnspecifiedError();
1463 }
1464
1465 /*
1466 * As define in the Chapter 28.3 - Chassis Control Command of IPMI
1467 * specification: It is recommended that no action occur if system
1468 * power is off (S4/S5) when this action is selected, and that a D5
1469 * "Requiest parameter(s) not supported in this presenst state."
1470 * error completion code be returned.
1471 */
1472 if (powerState.value() == false)
1473 {
1474 return ipmi::responseCommandNotAvailable();
1475 }
1476
1477 rc = initiateHostStateTransition(ctx,
1478 State::Host::Transition::Reboot);
1479 break;
1480 }
1481 case cmdSoftOffViaOverTemp:
1482 rc = initiateHostStateTransition(ctx, State::Host::Transition::Off);
1483 break;
1484 case cmdPulseDiagnosticInterrupt:
1485 rc = doNmi(ctx);
1486 break;
1487
1488 default:
1489 {
1490 lg2::error("Invalid Chassis Control command: {CMD}", "CMD",
1491 lg2::hex, chassisControl);
1492 return ipmi::responseInvalidFieldRequest();
1493 }
1494 }
1495
1496 return ((rc < 0) ? ipmi::responseUnspecifiedError()
1497 : ipmi::responseSuccess());
1498 }
1499
1500 /** @brief Return D-Bus connection string to enclosure identify LED object
1501 *
1502 * @param[in, out] connection - connection to D-Bus object
1503 * @return a IPMI return code
1504 */
getEnclosureIdentifyConnection()1505 std::string getEnclosureIdentifyConnection()
1506 {
1507 // lookup enclosure_identify group owner(s) in mapper
1508 try
1509 {
1510 return ipmi::getService(*getSdBus(), LedGroup::interface,
1511 identify_led_object_name);
1512 }
1513 catch (const std::exception& e)
1514 {
1515 lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}",
1516 "ERROR", e);
1517 elog<InternalFailure>();
1518 }
1519 }
1520
1521 /** @brief Turn On/Off enclosure identify LED
1522 *
1523 * @param[in] flag - true to turn on LED, false to turn off
1524 * @return a IPMI return code
1525 */
enclosureIdentifyLed(bool flag)1526 void enclosureIdentifyLed(bool flag)
1527 {
1528 using namespace chassis::internal;
1529
1530 std::string connection = getEnclosureIdentifyConnection();
1531
1532 lg2::debug("enclosureIdentifyLed({FLAG})", "FLAG", flag);
1533
1534 try
1535 {
1536 ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name,
1537 LedGroup::interface,
1538 LedGroup::property_names::asserted, flag);
1539 }
1540 catch (const std::exception& e)
1541 {
1542 lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}",
1543 "LED_STATE", flag, "ERROR", e);
1544 elog<InternalFailure>();
1545 }
1546 }
1547
1548 /** @brief Callback method to turn off LED
1549 */
enclosureIdentifyLedOff()1550 void enclosureIdentifyLedOff()
1551 {
1552 try
1553 {
1554 chassisIDState = ChassisIDState::off;
1555 enclosureIdentifyLed(false);
1556 }
1557 catch (const InternalFailure& e)
1558 {
1559 report<InternalFailure>();
1560 }
1561 }
1562
1563 /** @brief Create timer to turn on and off the enclosure LED
1564 */
createIdentifyTimer()1565 void createIdentifyTimer()
1566 {
1567 if (!identifyTimer)
1568 {
1569 identifyTimer =
1570 std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedOff);
1571 }
1572 }
1573
ipmiChassisIdentify(std::optional<uint8_t> interval,std::optional<uint8_t> force)1574 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1575 std::optional<uint8_t> force)
1576 {
1577 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1578 bool forceIdentify = force.value_or(0) & 0x01;
1579
1580 if (identifyInterval || forceIdentify)
1581 {
1582 // stop the timer if already started;
1583 // for force identify we should not turn off LED
1584 identifyTimer->stop();
1585 try
1586 {
1587 chassisIDState = ChassisIDState::temporaryOn;
1588 enclosureIdentifyLed(true);
1589 }
1590 catch (const InternalFailure& e)
1591 {
1592 report<InternalFailure>();
1593 return ipmi::responseResponseError();
1594 }
1595
1596 if (forceIdentify)
1597 {
1598 chassisIDState = ChassisIDState::indefiniteOn;
1599 return ipmi::responseSuccess();
1600 }
1601 // start the timer
1602 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1603 std::chrono::seconds(identifyInterval));
1604 identifyTimer->start(time);
1605 }
1606 else if (!identifyInterval)
1607 {
1608 identifyTimer->stop();
1609 enclosureIdentifyLedOff();
1610 }
1611 return ipmi::responseSuccess();
1612 }
1613
1614 namespace boot_options
1615 {
1616
1617 using namespace sdbusplus::server::xyz::openbmc_project::control::boot;
1618 using IpmiValue = uint8_t;
1619 constexpr auto ipmiDefault = 0;
1620
1621 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
1622 {0x01, Type::Types::EFI}};
1623
1624 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1625 {0x01, Source::Sources::Network},
1626 {0x02, Source::Sources::Disk},
1627 {0x05, Source::Sources::ExternalMedia},
1628 {0x0f, Source::Sources::RemovableMedia},
1629 {ipmiDefault, Source::Sources::Default}};
1630
1631 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1632 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1633 {0x03, Mode::Modes::Safe},
1634 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1635 {0x06, Mode::Modes::Setup},
1636 {ipmiDefault, Mode::Modes::Regular}};
1637
1638 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
1639 {Type::Types::EFI, 0x01}};
1640
1641 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1642 {Source::Sources::Network, 0x01},
1643 {Source::Sources::Disk, 0x02},
1644 {Source::Sources::ExternalMedia, 0x05},
1645 {Source::Sources::RemovableMedia, 0x0f},
1646 {Source::Sources::Default, ipmiDefault}};
1647
1648 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1649 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1650 {Mode::Modes::Safe, 0x03},
1651 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1652 {Mode::Modes::Setup, 0x06},
1653 {Mode::Modes::Regular, ipmiDefault}};
1654
1655 } // namespace boot_options
1656
1657 /** @brief Get the property value for boot source
1658 * @param[in] ctx - context pointer
1659 * @param[out] source - boot source value
1660 * @return On failure return IPMI error.
1661 */
getBootSource(ipmi::Context::ptr & ctx,Source::Sources & source)1662 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source)
1663 {
1664 using namespace chassis::internal;
1665 std::string result;
1666 std::string service;
1667 boost::system::error_code ec = getService(ctx, ControlBootSource::interface,
1668 bootSettingsPath, service);
1669 if (!ec)
1670 {
1671 ec = ipmi::getDbusProperty(
1672 ctx, service, bootSettingsPath, ControlBootSource::interface,
1673 ControlBootSource::property_names::boot_source, result);
1674 if (!ec)
1675 {
1676 source = Source::convertSourcesFromString(result);
1677 return ipmi::ccSuccess;
1678 }
1679 }
1680 lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message());
1681 return ipmi::ccUnspecifiedError;
1682 }
1683
1684 /** @brief Set the property value for boot source
1685 * @param[in] ctx - context pointer
1686 * @param[in] source - boot source value
1687 * @return On failure return IPMI error.
1688 */
setBootSource(ipmi::Context::ptr & ctx,const Source::Sources & source)1689 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx,
1690 const Source::Sources& source)
1691 {
1692 using namespace chassis::internal;
1693 std::string service;
1694 boost::system::error_code ec = getService(ctx, ControlBootSource::interface,
1695 bootSettingsPath, service);
1696 if (!ec)
1697 {
1698 ec = ipmi::setDbusProperty(
1699 ctx, service, bootSettingsPath, ControlBootSource::interface,
1700 ControlBootSource::property_names::boot_source,
1701 convertForMessage(source));
1702 if (!ec)
1703 {
1704 return ipmi::ccSuccess;
1705 }
1706 }
1707 lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message());
1708 return ipmi::ccUnspecifiedError;
1709 }
1710
1711 /** @brief Get the property value for boot mode
1712 * @param[in] ctx - context pointer
1713 * @param[out] mode - boot mode value
1714 * @return On failure return IPMI error.
1715 */
getBootMode(ipmi::Context::ptr & ctx,Mode::Modes & mode)1716 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode)
1717 {
1718 using namespace chassis::internal;
1719 std::string result;
1720 std::string service;
1721 boost::system::error_code ec =
1722 getService(ctx, ControlBootMode::interface, bootSettingsPath, service);
1723 if (!ec)
1724 {
1725 ec = ipmi::getDbusProperty(
1726 ctx, service, bootSettingsPath, ControlBootMode::interface,
1727 ControlBootMode::property_names::boot_mode, result);
1728 if (!ec)
1729 {
1730 mode = Mode::convertModesFromString(result);
1731 return ipmi::ccSuccess;
1732 }
1733 }
1734 lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message());
1735 return ipmi::ccUnspecifiedError;
1736 }
1737
1738 /** @brief Set the property value for boot mode
1739 * @param[in] ctx - context pointer
1740 * @param[in] mode - boot mode value
1741 * @return On failure return IPMI error.
1742 */
setBootMode(ipmi::Context::ptr & ctx,const Mode::Modes & mode)1743 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode)
1744 {
1745 using namespace chassis::internal;
1746 std::string service;
1747 boost::system::error_code ec =
1748 getService(ctx, ControlBootMode::interface, bootSettingsPath, service);
1749 if (!ec)
1750 {
1751 ec = ipmi::setDbusProperty(
1752 ctx, service, bootSettingsPath, ControlBootMode::interface,
1753 ControlBootMode::property_names::boot_mode,
1754 convertForMessage(mode));
1755 if (!ec)
1756 {
1757 return ipmi::ccSuccess;
1758 }
1759 }
1760 lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message());
1761 return ipmi::ccUnspecifiedError;
1762 }
1763
1764 /** @brief Get the property value for boot type
1765 * @param[in] ctx - context pointer
1766 * @param[out] type - boot type value
1767 * @return On failure return IPMI error.
1768 */
getBootType(ipmi::Context::ptr & ctx,Type::Types & type)1769 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type)
1770 {
1771 using namespace chassis::internal;
1772 std::string result;
1773 std::string service;
1774 boost::system::error_code ec =
1775 getService(ctx, ControlBootType::interface, bootSettingsPath, service);
1776
1777 // Don't throw error if BootType interface is not present.
1778 // This interface is not relevant for some Host architectures
1779 // (for example POWER). In this case we don't won't IPMI to
1780 // return an error, but simply return bootType as EFI.
1781 type = Type::Types::EFI;
1782 if (!ec)
1783 {
1784 ec = ipmi::getDbusProperty(
1785 ctx, service, bootSettingsPath, ControlBootType::interface,
1786 ControlBootType::property_names::boot_type, result);
1787 if (ec)
1788 {
1789 lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message());
1790 return ipmi::ccUnspecifiedError;
1791 }
1792 type = Type::convertTypesFromString(result);
1793 }
1794
1795 return ipmi::ccSuccess;
1796 }
1797
1798 /** @brief Set the property value for boot type
1799 * @param[in] ctx - context pointer
1800 * @param[in] type - boot type value
1801 * @return On failure return IPMI error.
1802 */
setBootType(ipmi::Context::ptr & ctx,const Type::Types & type)1803 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
1804 {
1805 using namespace chassis::internal;
1806 std::string service;
1807 boost::system::error_code ec =
1808 getService(ctx, ControlBootType::interface, bootSettingsPath, service);
1809 if (!ec)
1810 {
1811 ec = ipmi::setDbusProperty(
1812 ctx, service, bootSettingsPath, ControlBootType::interface,
1813 ControlBootType::property_names::boot_type,
1814 convertForMessage(type));
1815 if (ec)
1816 {
1817 lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message());
1818 return ipmi::ccUnspecifiedError;
1819 }
1820 }
1821 // Don't throw error if BootType interface is not present.
1822 // This interface is not relevant for some Host architectures
1823 // (for example POWER). In this case we don't won't IPMI to
1824 // return an error, but want to just skip this function.
1825 return ipmi::ccSuccess;
1826 }
1827
1828 /** @brief Get the property value for boot override enable
1829 * @param[in] ctx - context pointer
1830 * @param[out] enable - boot override enable
1831 * @return On failure return IPMI error.
1832 */
getBootEnable(ipmi::Context::ptr & ctx,bool & enable)1833 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable)
1834 {
1835 using namespace chassis::internal;
1836 std::string result;
1837 std::string service;
1838 boost::system::error_code ec =
1839 getService(ctx, ObjectEnable::interface, bootSettingsPath, service);
1840 if (!ec)
1841 {
1842 ec = ipmi::getDbusProperty(
1843 ctx, service, bootSettingsPath, ObjectEnable::interface,
1844 ObjectEnable::property_names::enabled, enable);
1845 if (!ec)
1846 {
1847 return ipmi::ccSuccess;
1848 }
1849 }
1850 lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR",
1851 ec.message());
1852 return ipmi::ccUnspecifiedError;
1853 }
1854
1855 /** @brief Set the property value for boot override enable
1856 * @param[in] ctx - context pointer
1857 * @param[in] enable - boot override enable
1858 * @return On failure return IPMI error.
1859 */
setBootEnable(ipmi::Context::ptr & ctx,const bool & enable)1860 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable)
1861 {
1862 using namespace chassis::internal;
1863 std::string service;
1864 boost::system::error_code ec =
1865 getService(ctx, ObjectEnable::interface, bootSettingsPath, service);
1866 if (!ec)
1867 {
1868 ec = ipmi::setDbusProperty(
1869 ctx, service, bootSettingsPath, ObjectEnable::interface,
1870 ObjectEnable::property_names::enabled, enable);
1871 if (!ec)
1872 {
1873 return ipmi::ccSuccess;
1874 }
1875 }
1876 lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR",
1877 ec.message());
1878 return ipmi::ccUnspecifiedError;
1879 }
1880
1881 /** @brief Get the property value for boot override one-time
1882 * @param[in] ctx - context pointer
1883 * @param[out] onetime - boot override one-time
1884 * @return On failure return IPMI error.
1885 */
getBootOneTime(ipmi::Context::ptr & ctx,bool & onetime)1886 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime)
1887 {
1888 using namespace chassis::internal;
1889 std::string result;
1890 std::string service;
1891 boost::system::error_code ec = getService(ctx, ObjectEnable::interface,
1892 bootSettingsOneTimePath, service);
1893 if (!ec)
1894 {
1895 ec = ipmi::getDbusProperty(
1896 ctx, service, bootSettingsOneTimePath, ObjectEnable::interface,
1897 ObjectEnable::property_names::enabled, onetime);
1898 if (!ec)
1899 {
1900 return ipmi::ccSuccess;
1901 }
1902 }
1903 lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR",
1904 ec.message());
1905 return ipmi::ccUnspecifiedError;
1906 }
1907
1908 /** @brief Set the property value for boot override one-time
1909 * @param[in] ctx - context pointer
1910 * @param[in] onetime - boot override one-time
1911 * @return On failure return IPMI error.
1912 */
setBootOneTime(ipmi::Context::ptr & ctx,const bool & onetime)1913 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime)
1914 {
1915 using namespace chassis::internal;
1916 std::string service;
1917 boost::system::error_code ec = getService(ctx, ObjectEnable::interface,
1918 bootSettingsOneTimePath, service);
1919 if (!ec)
1920 {
1921 ec = ipmi::setDbusProperty(
1922 ctx, service, bootSettingsOneTimePath, ObjectEnable::interface,
1923 ObjectEnable::property_names::enabled, onetime);
1924 if (!ec)
1925 {
1926 return ipmi::ccSuccess;
1927 }
1928 }
1929 lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR",
1930 ec.message());
1931 return ipmi::ccUnspecifiedError;
1932 }
1933
1934 static constexpr uint8_t setComplete = 0x0;
1935 static constexpr uint8_t setInProgress = 0x1;
1936 static uint8_t transferStatus = setComplete;
1937 static uint8_t bootFlagValidBitClr = 0;
1938 static uint5_t bootInitiatorAckData = 0x0;
1939 static bool cmosClear = false;
1940 static uint2_t biosVerbosity = 0x0;
1941
1942 /** @brief implements the Get Chassis system boot option
1943 * @param ctx - context pointer
1944 * @param bootOptionParameter - boot option parameter selector
1945 * @param reserved1 - reserved bit
1946 * @param setSelector - selects a particular block or set of parameters
1947 * under the given parameter selector
1948 * write as 00h if parameter doesn't use a setSelector
1949 * @param blockSelector- selects a particular block within a set of
1950 * parameters write as 00h if parameter doesn't use a
1951 * blockSelector
1952 *
1953 * @return IPMI completion code plus response data
1954 * @return Payload contains below parameters:
1955 * version - parameter version
1956 * bootOptionParameter - boot option parameter selector
1957 * parmIndicator - parameter valid/invalid indicator
1958 * data - configuration parameter data
1959 */
ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx,uint7_t bootOptionParameter,bool reserved1,uint8_t setSelector,uint8_t blockSelector)1960 ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions(
1961 ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1,
1962 [[maybe_unused]] uint8_t setSelector,
1963 [[maybe_unused]] uint8_t blockSelector)
1964 {
1965 ipmi::Cc rc;
1966 if (reserved1)
1967 {
1968 return ipmi::responseInvalidFieldRequest();
1969 }
1970
1971 constexpr uint4_t version = 0x01;
1972 ipmi::message::Payload response;
1973 response.pack(version, uint4_t{});
1974 using namespace boot_options;
1975
1976 IpmiValue bootOption = ipmiDefault;
1977
1978 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1979 BootOptionParameter::setInProgress)
1980 {
1981 response.pack(bootOptionParameter, reserved1, transferStatus);
1982 return ipmi::responseSuccess(std::move(response));
1983 }
1984
1985 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1986 BootOptionParameter::bootInfo)
1987 {
1988 constexpr uint8_t writeMask = 0;
1989 response.pack(bootOptionParameter, reserved1, writeMask,
1990 bootInitiatorAckData);
1991 return ipmi::responseSuccess(std::move(response));
1992 }
1993
1994 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1995 BootOptionParameter::bootFlagValidClr)
1996 {
1997 response.pack(bootOptionParameter, reserved1,
1998 uint5_t{bootFlagValidBitClr}, uint3_t{});
1999 return ipmi::responseSuccess(std::move(response));
2000 }
2001
2002 /*
2003 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2004 * This is the only parameter used by petitboot.
2005 */
2006 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
2007 BootOptionParameter::bootFlags)
2008 {
2009 using namespace chassis::internal;
2010 using namespace chassis::internal::cache;
2011
2012 try
2013 {
2014 Source::Sources bootSource;
2015 rc = getBootSource(ctx, bootSource);
2016 if (rc != ipmi::ccSuccess)
2017 {
2018 return ipmi::response(rc);
2019 }
2020
2021 Type::Types bootType;
2022 rc = getBootType(ctx, bootType);
2023 if (rc != ipmi::ccSuccess)
2024 {
2025 return ipmi::response(rc);
2026 }
2027
2028 Mode::Modes bootMode;
2029 rc = getBootMode(ctx, bootMode);
2030 if (rc != ipmi::ccSuccess)
2031 {
2032 return ipmi::response(rc);
2033 }
2034
2035 bootOption = sourceDbusToIpmi.at(bootSource);
2036 if ((Mode::Modes::Regular == bootMode) &&
2037 (Source::Sources::Default == bootSource))
2038 {
2039 bootOption = ipmiDefault;
2040 }
2041 else if (Source::Sources::Default == bootSource)
2042 {
2043 bootOption = modeDbusToIpmi.at(bootMode);
2044 }
2045
2046 IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
2047
2048 bool oneTimeEnabled;
2049 rc = getBootOneTime(ctx, oneTimeEnabled);
2050 if (rc != ipmi::ccSuccess)
2051 {
2052 return ipmi::response(rc);
2053 }
2054
2055 uint1_t permanent = oneTimeEnabled ? 0 : 1;
2056
2057 bool valid;
2058 rc = getBootEnable(ctx, valid);
2059 if (rc != ipmi::ccSuccess)
2060 {
2061 return ipmi::response(rc);
2062 }
2063
2064 uint1_t validFlag = valid ? 1 : 0;
2065
2066 response.pack(
2067 bootOptionParameter, reserved1, uint5_t{},
2068 uint1_t{biosBootType}, uint1_t{permanent}, uint1_t{validFlag},
2069 uint2_t{}, uint4_t{bootOption}, uint1_t{}, cmosClear, uint5_t{},
2070 uint2_t{biosVerbosity}, uint1_t{}, uint8_t{}, uint8_t{});
2071 return ipmi::responseSuccess(std::move(response));
2072 }
2073 catch (const InternalFailure& e)
2074 {
2075 cache::objectsPtr.reset();
2076 report<InternalFailure>();
2077 return ipmi::responseUnspecifiedError();
2078 }
2079 }
2080 else
2081 {
2082 if ((bootOptionParameter >= oemParmStart) &&
2083 (bootOptionParameter <= oemParmEnd))
2084 {
2085 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
2086 BootOptionParameter::opalNetworkSettings)
2087 {
2088 response.pack(bootOptionParameter, reserved1);
2089 int ret = getHostNetworkData(response);
2090 if (ret < 0)
2091 {
2092 response.trailingOk = true;
2093 lg2::error(
2094 "getHostNetworkData failed for GetSysBootOptions.");
2095 return ipmi::responseUnspecifiedError();
2096 }
2097 else
2098 {
2099 return ipmi::responseSuccess(std::move(response));
2100 }
2101 }
2102 else
2103 {
2104 lg2::error(
2105 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
2106 "PARAM", lg2::hex,
2107 static_cast<uint8_t>(bootOptionParameter));
2108 return ipmi::responseParmNotSupported();
2109 }
2110 }
2111 else
2112 {
2113 lg2::error(
2114 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
2115 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter));
2116 return ipmi::responseParmNotSupported();
2117 }
2118 }
2119 return ipmi::responseUnspecifiedError();
2120 }
2121
ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,uint7_t parameterSelector,bool,ipmi::message::Payload & data)2122 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
2123 uint7_t parameterSelector, bool,
2124 ipmi::message::Payload& data)
2125 {
2126 using namespace boot_options;
2127 ipmi::Cc rc;
2128
2129 if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2130 BootOptionParameter::setInProgress)
2131 {
2132 uint2_t setInProgressFlag;
2133 uint6_t rsvd;
2134 if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked())
2135 {
2136 return ipmi::responseReqDataLenInvalid();
2137 }
2138 if (rsvd)
2139 {
2140 return ipmi::responseInvalidFieldRequest();
2141 }
2142 if ((transferStatus == setInProgress) &&
2143 (static_cast<uint8_t>(setInProgressFlag) != setComplete))
2144 {
2145 return ipmi::responsefailSetInProgress();
2146 }
2147 transferStatus = static_cast<uint8_t>(setInProgressFlag);
2148 return ipmi::responseSuccess();
2149 }
2150
2151 /* 000101
2152 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2153 * This is the only parameter used by petitboot.
2154 */
2155
2156 if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2157 BootOptionParameter::bootFlags)
2158 {
2159 uint5_t rsvd;
2160 bool validFlag;
2161 bool permanent;
2162 bool biosBootType;
2163 bool lockOutResetButton;
2164 bool screenBlank;
2165 uint4_t bootDeviceSelector;
2166 bool lockKeyboard;
2167 uint5_t biosCtrls;
2168 bool lockOutPower;
2169 uint4_t biosInfo;
2170 uint4_t rsvd1;
2171 uint5_t deviceInstance;
2172 uint3_t rsvd2;
2173
2174 if (data.unpack(rsvd, biosBootType, permanent, validFlag,
2175 lockOutResetButton, screenBlank, bootDeviceSelector,
2176 lockKeyboard, cmosClear, biosCtrls, biosVerbosity,
2177 lockOutPower, biosInfo, rsvd1, deviceInstance, rsvd2) !=
2178 0 ||
2179 !data.fullyUnpacked())
2180 {
2181 return ipmi::responseReqDataLenInvalid();
2182 }
2183 if (rsvd || rsvd1 || rsvd2)
2184 {
2185 return ipmi::responseInvalidFieldRequest();
2186 }
2187
2188 using namespace chassis::internal;
2189 using namespace chassis::internal::cache;
2190
2191 try
2192 {
2193 rc = setBootOneTime(ctx, !permanent);
2194 if (rc != ipmi::ccSuccess)
2195 {
2196 return ipmi::response(rc);
2197 }
2198
2199 rc = setBootEnable(ctx, validFlag);
2200 if (rc != ipmi::ccSuccess)
2201 {
2202 return ipmi::response(rc);
2203 }
2204
2205 auto modeItr =
2206 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2207 auto typeItr =
2208 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
2209 auto sourceItr =
2210 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2211 if (sourceIpmiToDbus.end() != sourceItr)
2212 {
2213 rc = setBootSource(ctx, sourceItr->second);
2214 if (rc != ipmi::ccSuccess)
2215 {
2216 return ipmi::response(rc);
2217 }
2218 // If a set boot device is mapping to a boot source, then reset
2219 // the boot mode D-Bus property to default.
2220 // This way the ipmid code can determine which property is not
2221 // at the default value
2222 if (sourceItr->second != Source::Sources::Default)
2223 {
2224 rc = setBootMode(ctx, Mode::Modes::Regular);
2225 if (rc != ipmi::ccSuccess)
2226 {
2227 return ipmi::response(rc);
2228 }
2229 }
2230 }
2231
2232 if (typeIpmiToDbus.end() != typeItr)
2233 {
2234 rc = setBootType(ctx, typeItr->second);
2235 if (rc != ipmi::ccSuccess)
2236 {
2237 return ipmi::response(rc);
2238 }
2239 }
2240
2241 if (modeIpmiToDbus.end() != modeItr)
2242 {
2243 rc = setBootMode(ctx, modeItr->second);
2244 if (rc != ipmi::ccSuccess)
2245 {
2246 return ipmi::response(rc);
2247 }
2248 // If a set boot device is mapping to a boot mode, then reset
2249 // the boot source D-Bus property to default.
2250 // This way the ipmid code can determine which property is not
2251 // at the default value
2252 if (modeItr->second != Mode::Modes::Regular)
2253 {
2254 rc = setBootSource(ctx, Source::Sources::Default);
2255 if (rc != ipmi::ccSuccess)
2256 {
2257 return ipmi::response(rc);
2258 }
2259 }
2260 }
2261 if ((modeIpmiToDbus.end() == modeItr) &&
2262 (typeIpmiToDbus.end() == typeItr) &&
2263 (sourceIpmiToDbus.end() == sourceItr))
2264 {
2265 // return error if boot option is not supported
2266 lg2::error(
2267 "ipmiChassisSetSysBootOptions: Boot option not supported");
2268 return ipmi::responseInvalidFieldRequest();
2269 }
2270 }
2271 catch (const sdbusplus::exception_t& e)
2272 {
2273 objectsPtr.reset();
2274 report<InternalFailure>();
2275 lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot "
2276 "flag parameters");
2277 return ipmi::responseUnspecifiedError();
2278 }
2279 }
2280 else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2281 BootOptionParameter::bootInfo)
2282 {
2283 uint8_t writeMak;
2284 uint5_t bootInfoAck;
2285 uint3_t rsvd;
2286
2287 if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 ||
2288 !data.fullyUnpacked())
2289 {
2290 return ipmi::responseReqDataLenInvalid();
2291 }
2292 if (rsvd)
2293 {
2294 return ipmi::responseInvalidFieldRequest();
2295 }
2296 bootInitiatorAckData &= ~writeMak;
2297 bootInitiatorAckData |= (writeMak & bootInfoAck);
2298 lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set "
2299 "successfully");
2300 data.trailingOk = true;
2301 return ipmi::responseSuccess();
2302 }
2303 else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2304 BootOptionParameter::bootFlagValidClr)
2305 {
2306 uint5_t bootFlagValidClr;
2307 uint3_t rsvd;
2308
2309 if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked())
2310 {
2311 return ipmi::responseReqDataLenInvalid();
2312 }
2313 if (rsvd)
2314 {
2315 return ipmi::responseInvalidFieldRequest();
2316 }
2317 // store boot flag valid bits clear value
2318 bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr);
2319 lg2::info(
2320 "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set "
2321 "successfully to {VALUE}",
2322 "VALUE", lg2::hex, bootFlagValidBitClr);
2323 return ipmi::responseSuccess();
2324 }
2325 else
2326 {
2327 if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
2328 (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
2329 {
2330 if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2331 BootOptionParameter::opalNetworkSettings)
2332 {
2333 ipmi::Cc ret = setHostNetworkData(data);
2334 if (ret != ipmi::ccSuccess)
2335 {
2336 lg2::error("ipmiChassisSetSysBootOptions: Error in "
2337 "setHostNetworkData");
2338 data.trailingOk = true;
2339 return ipmi::response(ret);
2340 }
2341 data.trailingOk = true;
2342 return ipmi::responseSuccess();
2343 }
2344 else
2345 {
2346 lg2::error(
2347 "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}",
2348 "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector));
2349 data.trailingOk = true;
2350 return ipmi::responseParmNotSupported();
2351 }
2352 }
2353 data.trailingOk = true;
2354 return ipmi::responseParmNotSupported();
2355 }
2356 return ipmi::responseSuccess();
2357 }
2358
2359 /** @brief implements Get POH counter command
2360 * @parameter
2361 * - none
2362 * @returns IPMI completion code plus response data
2363 * - minPerCount - Minutes per count
2364 * - counterReading - counter reading
2365 */
2366 ipmi::RspType<uint8_t, // Minutes per count
2367 uint32_t // Counter reading
2368 >
ipmiGetPOHCounter()2369 ipmiGetPOHCounter()
2370 {
2371 // sd_bus error
2372 try
2373 {
2374 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
2375 getPOHCounter());
2376 }
2377 catch (const std::exception& e)
2378 {
2379 lg2::error("getPOHCounter error: {ERROR}", "ERROR", e);
2380 return ipmi::responseUnspecifiedError();
2381 }
2382 }
2383
2384 ipmi::RspType<uint3_t, // policy support
2385 uint5_t // reserved
2386 >
ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx,uint3_t policy,uint5_t reserved)2387 ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx, uint3_t policy,
2388 uint5_t reserved)
2389 {
2390 power_policy::DbusValue value =
2391 power_policy::RestorePolicy::Policy::AlwaysOff;
2392
2393 if (reserved || (policy > power_policy::noChange))
2394 {
2395 lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex,
2396 static_cast<int>(policy));
2397 return ipmi::responseInvalidFieldRequest();
2398 }
2399
2400 if (policy == power_policy::noChange)
2401 {
2402 // just return the supported policy
2403 return ipmi::responseSuccess(power_policy::allSupport, reserved);
2404 }
2405
2406 for (const auto& it : power_policy::dbusToIpmi)
2407 {
2408 if (it.second == policy)
2409 {
2410 value = it.first;
2411 break;
2412 }
2413 }
2414
2415 try
2416 {
2417 settings::Objects& objects = chassis::internal::cache::getObjects();
2418 const settings::Path& powerRestoreSetting =
2419 objects.map.at(ControlPowerRestorePolicy::interface).front();
2420 const auto& powerRestoreSettingService =
2421 objects.serviceMap.at(powerRestoreSetting);
2422
2423 boost::system::error_code ec = ipmi::setDbusProperty(
2424 ctx, powerRestoreSettingService, powerRestoreSetting,
2425 ControlPowerRestorePolicy::interface,
2426 ControlPowerRestorePolicy::property_names::power_restore_policy,
2427 convertForMessage(value));
2428 if (ec)
2429 {
2430 lg2::error("Unspecified Error");
2431 return ipmi::responseUnspecifiedError();
2432 }
2433 }
2434 catch (const InternalFailure& e)
2435 {
2436 chassis::internal::cache::objectsPtr.reset();
2437 report<InternalFailure>();
2438 return ipmi::responseUnspecifiedError();
2439 }
2440
2441 return ipmi::responseSuccess(power_policy::allSupport, reserved);
2442 }
2443
ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx,bool disablePowerButton,bool disableResetButton,bool,bool,uint4_t)2444 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
2445 ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
2446 bool, bool, uint4_t)
2447 {
2448 using namespace chassis::internal;
2449
2450 // set power button Enabled property
2451 bool success =
2452 setButtonDisabled(ctx, powerButtonPath, ChassisButtonsPower::interface,
2453 disablePowerButton);
2454
2455 // set reset button Enabled property
2456 success &=
2457 setButtonDisabled(ctx, resetButtonPath, ChassisButtonsReset::interface,
2458 disableResetButton);
2459
2460 if (!success)
2461 {
2462 // not all buttons were successfully set
2463 return ipmi::responseUnspecifiedError();
2464 }
2465 return ipmi::responseSuccess();
2466 }
2467
registerNetFnChassisFunctions()2468 void registerNetFnChassisFunctions()
2469 {
2470 createIdentifyTimer();
2471
2472 // Get Chassis Capabilities
2473 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2474 ipmi::chassis::cmdGetChassisCapabilities,
2475 ipmi::Privilege::User, ipmiGetChassisCap);
2476
2477 // Set Front Panel Button Enables
2478 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2479 ipmi::chassis::cmdSetFrontPanelButtonEnables,
2480 ipmi::Privilege::Admin,
2481 ipmiSetFrontPanelButtonEnables);
2482
2483 // Set Chassis Capabilities
2484 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2485 ipmi::chassis::cmdSetChassisCapabilities,
2486 ipmi::Privilege::User, ipmiSetChassisCap);
2487
2488 // <Get System Boot Options>
2489 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2490 ipmi::chassis::cmdGetSystemBootOptions,
2491 ipmi::Privilege::Operator,
2492 ipmiChassisGetSysBootOptions);
2493
2494 // <Get Chassis Status>
2495 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2496 ipmi::chassis::cmdGetChassisStatus,
2497 ipmi::Privilege::User, ipmiGetChassisStatus);
2498
2499 // <Chassis Get System Restart Cause>
2500 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2501 ipmi::chassis::cmdGetSystemRestartCause,
2502 ipmi::Privilege::User, ipmiGetSystemRestartCause);
2503
2504 // <Chassis Control>
2505 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2506 ipmi::chassis::cmdChassisControl,
2507 ipmi::Privilege::Operator, ipmiChassisControl);
2508
2509 // <Chassis Identify>
2510 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2511 ipmi::chassis::cmdChassisIdentify,
2512 ipmi::Privilege::Operator, ipmiChassisIdentify);
2513
2514 // <Set System Boot Options>
2515 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2516 ipmi::chassis::cmdSetSystemBootOptions,
2517 ipmi::Privilege::Operator,
2518 ipmiChassisSetSysBootOptions);
2519
2520 // <Get POH Counter>
2521 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2522 ipmi::chassis::cmdGetPohCounter,
2523 ipmi::Privilege::User, ipmiGetPOHCounter);
2524
2525 // <Set Power Restore Policy>
2526 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2527 ipmi::chassis::cmdSetPowerRestorePolicy,
2528 ipmi::Privilege::Operator,
2529 ipmiChassisSetPowerRestorePolicy);
2530 }
2531