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