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