1 /*
2 // Copyright (c) 2019 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #include "xyz/openbmc_project/Common/error.hpp"
17
18 #include <ipmid/api.hpp>
19 #include <ipmid/utils.hpp>
20 #include <nlohmann/json.hpp>
21 #include <phosphor-logging/elog-errors.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/timer.hpp>
24 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
25
26 #include <fstream>
27 #include <iostream>
28 #include <regex>
29 #include <stdexcept>
30 #include <string_view>
31
32 using namespace phosphor::logging;
33
34 namespace ipmi::chassis
35 {
36 static constexpr const char* buttonIntf = "xyz.openbmc_project.Chassis.Buttons";
37
38 const static constexpr char* idButtonPath =
39 "/xyz/openbmc_project/chassis/buttons/id";
40 static constexpr const char* powerButtonPath =
41 "/xyz/openbmc_project/chassis/buttons/power";
42 static constexpr const char* resetButtonPath =
43 "/xyz/openbmc_project/chassis/buttons/reset";
44 static constexpr const char* interruptButtonPath =
45 "/xyz/openbmc_project/chassis/buttons/nmi";
46
47 const static constexpr char* idButtonProp = "ButtonPressed";
48
49 const static constexpr char* ledService =
50 "xyz.openbmc_project.LED.GroupManager";
51 const static constexpr char* ledIDOnObj =
52 "/xyz/openbmc_project/led/groups/enclosure_identify";
53 const static constexpr char* ledIDBlinkObj =
54 "/xyz/openbmc_project/led/groups/enclosure_identify_blink";
55 const static constexpr char* ledInterface = "xyz.openbmc_project.Led.Group";
56 const static constexpr char* ledProp = "Asserted";
57 enum class ChassisIDState
58 {
59 off = 0,
60 temporary = 1,
61 indefinite = 2,
62 };
63 static ChassisIDState chassisIDState = ChassisIDState::off;
64
65 constexpr size_t defaultIdentifyTimeOut = 15;
66
67 std::unique_ptr<sdbusplus::Timer> identifyTimer
68 __attribute__((init_priority(101)));
69 std::unique_ptr<sdbusplus::bus::match_t> matchPtr
70 __attribute__((init_priority(101)));
71
72 static void registerChassisFunctions() __attribute__((constructor));
73
74 static ipmi::ServiceCache LEDService(ledInterface, ledIDBlinkObj);
75
enclosureIdentifyLed(const char * objName,bool isIdLedOn)76 void enclosureIdentifyLed(const char* objName, bool isIdLedOn)
77 {
78 auto bus = getSdBus();
79
80 try
81 {
82 std::string service = LEDService.getService(*bus);
83 setDbusProperty(*bus, service, objName, ledInterface, ledProp,
84 isIdLedOn);
85 }
86 catch (const std::exception& e)
87 {
88 log<level::ERR>("enclosureIdentifyLed: can't set property",
89 entry("ERR=%s", e.what()));
90 }
91 }
92
getIDState(const char * objName,bool & state)93 bool getIDState(const char* objName, bool& state)
94 {
95 auto bus = getSdBus();
96
97 try
98 {
99 std::string service = LEDService.getService(*bus);
100 ipmi::Value enabled =
101 getDbusProperty(*bus, service, objName, ledInterface, ledProp);
102 state = std::get<bool>(enabled);
103 }
104 catch (const sdbusplus::exception_t& e)
105 {
106 log<level::ERR>("Fail to get property", entry("PATH=%s", objName),
107 entry("ERROR=%s", e.what()));
108 return false;
109 }
110 return true;
111 }
112
enclosureIdentifyLedBlinkOff()113 void enclosureIdentifyLedBlinkOff()
114 {
115 chassisIDState = ChassisIDState::off;
116 enclosureIdentifyLed(ledIDBlinkObj, false);
117 }
118
idButtonPropChanged(sdbusplus::message_t & msg)119 void idButtonPropChanged(sdbusplus::message_t& msg)
120 {
121 bool asserted = false;
122 bool buttonPressed = false;
123
124 std::map<std::string, ipmi::Value> props;
125 std::vector<std::string> inval;
126 std::string iface;
127 msg.read(iface, props, inval);
128
129 for (const auto& t : props)
130 {
131 auto key = t.first;
132 auto value = t.second;
133
134 if (key == idButtonProp)
135 {
136 buttonPressed = std::get<bool>(value);
137 }
138 break;
139 }
140
141 if (buttonPressed)
142 {
143 if (identifyTimer->isRunning())
144 {
145 log<level::INFO>("ID timer is running");
146 }
147
148 // make sure timer is stopped
149 identifyTimer->stop();
150
151 if (!getIDState(ledIDBlinkObj, asserted))
152 {
153 return;
154 }
155
156 if (asserted)
157 {
158 // LED is blinking, turn off the LED
159 chassisIDState = ChassisIDState::off;
160 enclosureIdentifyLed(ledIDBlinkObj, false);
161 enclosureIdentifyLed(ledIDOnObj, false);
162 }
163 else
164 {
165 // toggle the IED on/off
166 if (!getIDState(ledIDOnObj, asserted))
167 {
168 return;
169 }
170 enclosureIdentifyLed(ledIDOnObj, !asserted);
171 }
172 }
173 }
174
createIdentifyTimer()175 void createIdentifyTimer()
176 {
177 if (!identifyTimer)
178 {
179 identifyTimer =
180 std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedBlinkOff);
181 }
182 }
183
ipmiChassisIdentify(std::optional<uint8_t> interval,std::optional<uint8_t> force)184 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
185 std::optional<uint8_t> force)
186 {
187 uint8_t identifyInterval = interval.value_or(defaultIdentifyTimeOut);
188 bool forceIdentify = force.value_or(0) & 0x01;
189
190 enclosureIdentifyLed(ledIDOnObj, false);
191 identifyTimer->stop();
192
193 if (identifyInterval || forceIdentify)
194 {
195 enclosureIdentifyLed(ledIDBlinkObj, true);
196 if (forceIdentify)
197 {
198 chassisIDState = ChassisIDState::indefinite;
199 return ipmi::responseSuccess();
200 }
201 chassisIDState = ChassisIDState::temporary;
202 // start the timer
203 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
204 std::chrono::seconds(identifyInterval));
205 identifyTimer->start(time);
206 }
207 else
208 {
209 chassisIDState = ChassisIDState::off;
210 enclosureIdentifyLed(ledIDBlinkObj, false);
211 }
212 return ipmi::responseSuccess();
213 }
214
215 namespace power_policy
216 {
217 /* helper function for Get Chassis Status Command
218 */
getPowerRestorePolicy()219 std::optional<uint2_t> getPowerRestorePolicy()
220 {
221 constexpr const char* powerRestorePath =
222 "/xyz/openbmc_project/control/host0/power_restore_policy";
223 constexpr const char* powerRestoreIntf =
224 "xyz.openbmc_project.Control.Power.RestorePolicy";
225 uint2_t restorePolicy = 0;
226 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
227
228 try
229 {
230 auto service =
231 ipmi::getService(*busp, powerRestoreIntf, powerRestorePath);
232
233 ipmi::Value result =
234 ipmi::getDbusProperty(*busp, service, powerRestorePath,
235 powerRestoreIntf, "PowerRestorePolicy");
236 auto powerRestore = sdbusplus::xyz::openbmc_project::Control::Power::
237 server::RestorePolicy::convertPolicyFromString(
238 std::get<std::string>(result));
239
240 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
241 switch (powerRestore)
242 {
243 case RestorePolicy::Policy::AlwaysOff:
244 restorePolicy = 0x00;
245 break;
246 case RestorePolicy::Policy::Restore:
247 restorePolicy = 0x01;
248 break;
249 case RestorePolicy::Policy::AlwaysOn:
250 restorePolicy = 0x02;
251 break;
252 default:
253 break;
254 }
255 }
256 catch (const std::exception& e)
257 {
258 log<level::ERR>("Failed to fetch PowerRestorePolicy property",
259 entry("ERROR=%s", e.what()),
260 entry("PATH=%s", powerRestorePath),
261 entry("INTERFACE=%s", powerRestoreIntf));
262 return std::nullopt;
263 }
264 return std::make_optional(restorePolicy);
265 }
266
267 /*
268 * getPowerStatus
269 * helper function for Get Chassis Status Command
270 * return - optional value for pgood (no value on error)
271 */
getPowerStatus()272 std::optional<bool> getPowerStatus()
273 {
274 bool powerGood = false;
275 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
276 try
277 {
278 constexpr const char* chassisStatePath =
279 "/xyz/openbmc_project/state/chassis0";
280 constexpr const char* chassisStateIntf =
281 "xyz.openbmc_project.State.Chassis";
282 auto service =
283 ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
284
285 ipmi::Value variant =
286 ipmi::getDbusProperty(*busp, service, chassisStatePath,
287 chassisStateIntf, "CurrentPowerState");
288 std::string powerState = std::get<std::string>(variant);
289 if (powerState == "xyz.openbmc_project.State.Chassis.PowerState.On")
290 {
291 powerGood = true;
292 }
293 }
294 catch (const std::exception& e)
295 {
296 log<level::ERR>("Failed to fetch power state property",
297 entry("ERROR=%s", e.what()));
298 return std::nullopt;
299 }
300 return std::make_optional(powerGood);
301 }
302
303 /*
304 * getACFailStatus
305 * helper function for Get Chassis Status Command
306 * return - bool value for ACFail (false on error)
307 */
getACFailStatus()308 bool getACFailStatus()
309 {
310 constexpr const char* acBootObj =
311 "/xyz/openbmc_project/control/host0/ac_boot";
312 constexpr const char* acBootIntf = "xyz.openbmc_project.Common.ACBoot";
313 std::string acFail;
314 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
315 try
316 {
317 auto service = ipmi::getService(*bus, acBootIntf, acBootObj);
318
319 ipmi::Value variant = ipmi::getDbusProperty(*bus, service, acBootObj,
320 acBootIntf, "ACBoot");
321 acFail = std::get<std::string>(variant);
322 }
323 catch (const std::exception& e)
324 {
325 log<level::ERR>(
326 "Failed to fetch ACBoot property", entry("ERROR=%s", e.what()),
327 entry("PATH=%s", acBootObj), entry("INTERFACE=%s", acBootIntf));
328 }
329 return acFail == "True";
330 }
331 } // namespace power_policy
332
getButtonEnabled(const std::string & buttonPath)333 static std::optional<bool> getButtonEnabled(const std::string& buttonPath)
334 {
335 bool buttonDisabled = false;
336 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
337 try
338 {
339 auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
340 ipmi::Value disabled = ipmi::getDbusProperty(
341 *busp, service, buttonPath, buttonIntf, "ButtonMasked");
342 buttonDisabled = std::get<bool>(disabled);
343 }
344 catch (const sdbusplus::exception_t& e)
345 {
346 log<level::ERR>("Fail to get button disabled property",
347 entry("PATH=%s", buttonPath.c_str()),
348 entry("ERROR=%s", e.what()));
349 return std::nullopt;
350 }
351 return std::make_optional(buttonDisabled);
352 }
353
setButtonEnabled(const std::string & buttonPath,const bool disabled)354 static bool setButtonEnabled(const std::string& buttonPath, const bool disabled)
355 {
356 try
357 {
358 auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
359 ipmi::setDbusProperty(*getSdBus(), service, buttonPath, buttonIntf,
360 "ButtonMasked", disabled);
361 }
362 catch (const std::exception& e)
363 {
364 log<level::ERR>("Failed to set button disabled",
365 entry("EXCEPTION=%s, REQUEST=%x", e.what(), disabled));
366 return -1;
367 }
368
369 return 0;
370 }
371
getRestartCause(ipmi::Context::ptr & ctx,std::string & restartCause)372 static bool getRestartCause(ipmi::Context::ptr& ctx, std::string& restartCause)
373 {
374 constexpr const char* restartCausePath =
375 "/xyz/openbmc_project/control/host0/restart_cause";
376 constexpr const char* restartCauseIntf =
377 "xyz.openbmc_project.Control.Host.RestartCause";
378
379 std::string service;
380 boost::system::error_code ec =
381 ipmi::getService(ctx, restartCauseIntf, restartCausePath, service);
382
383 if (!ec)
384 {
385 ec = ipmi::getDbusProperty(ctx, service, restartCausePath,
386 restartCauseIntf, "RestartCause",
387 restartCause);
388 }
389 if (ec)
390 {
391 log<level::ERR>("Failed to fetch RestartCause property",
392 entry("ERROR=%s", ec.message().c_str()),
393 entry("PATH=%s", restartCausePath),
394 entry("INTERFACE=%s", restartCauseIntf));
395 return false;
396 }
397 return true;
398 }
399
checkIPMIRestartCause(ipmi::Context::ptr & ctx,bool & ipmiRestartCause)400 static bool checkIPMIRestartCause(ipmi::Context::ptr& ctx,
401 bool& ipmiRestartCause)
402 {
403 std::string restartCause;
404 if (!getRestartCause(ctx, restartCause))
405 {
406 return false;
407 }
408 ipmiRestartCause =
409 (restartCause ==
410 "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand");
411 return true;
412 }
413
414 //----------------------------------------------------------------------
415 // Get Chassis Status commands
416 //----------------------------------------------------------------------
417 ipmi::RspType<bool, // Power is on
418 bool, // Power overload
419 bool, // Interlock
420 bool, // power fault
421 bool, // power control fault
422 uint2_t, // power restore policy
423 bool, // reserved
424
425 bool, // AC failed
426 bool, // last power down caused by a Power overload
427 bool, // last power down caused by a power interlock
428 bool, // last power down caused by power fault
429 bool, // last ‘Power is on’ state was entered via IPMI command
430 uint3_t, // reserved
431
432 bool, // Chassis intrusion active
433 bool, // Front Panel Lockout active
434 bool, // Drive Fault
435 bool, // Cooling/fan fault detected
436 uint2_t, // Chassis Identify State
437 bool, // Chassis Identify command and state info supported
438 bool, // reserved
439
440 bool, // Power off button disabled
441 bool, // Reset button disabled
442 bool, // Diagnostic Interrupt button disabled
443 bool, // Standby (sleep) button disabled
444 bool, // Power off button disable allowed
445 bool, // Reset button disable allowed
446 bool, // Diagnostic Interrupt button disable allowed
447 bool // Standby (sleep) button disable allowed
448 >
ipmiGetChassisStatus(ipmi::Context::ptr ctx)449 ipmiGetChassisStatus(ipmi::Context::ptr ctx)
450 {
451 std::optional<uint2_t> restorePolicy =
452 power_policy::getPowerRestorePolicy();
453 std::optional<bool> powerGood = power_policy::getPowerStatus();
454 if (!restorePolicy || !powerGood)
455 {
456 return ipmi::responseUnspecifiedError();
457 }
458
459 // Front Panel Button Capabilities and disable/enable status(Optional)
460 std::optional<bool> powerButtonReading = getButtonEnabled(powerButtonPath);
461 // allow disable if the interface is present
462 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
463 // default return the button is enabled (not disabled)
464 bool powerButtonDisabled = false;
465 if (powerButtonDisableAllow)
466 {
467 // return the real value of the button status, if present
468 powerButtonDisabled = *powerButtonReading;
469 }
470
471 std::optional<bool> resetButtonReading = getButtonEnabled(resetButtonPath);
472 // allow disable if the interface is present
473 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
474 // default return the button is enabled (not disabled)
475 bool resetButtonDisabled = false;
476 if (resetButtonDisableAllow)
477 {
478 // return the real value of the button status, if present
479 resetButtonDisabled = *resetButtonReading;
480 }
481
482 std::optional<bool> interruptButtonReading =
483 getButtonEnabled(interruptButtonPath);
484 // allow disable if the interface is present
485 bool interruptButtonDisableAllow =
486 static_cast<bool>(interruptButtonReading);
487 // default return the button is enabled (not disabled)
488 bool interruptButtonDisabled = false;
489 if (interruptButtonDisableAllow)
490 {
491 // return the real value of the button status, if present
492 interruptButtonDisabled = *interruptButtonReading;
493 }
494
495 bool powerDownAcFailed = power_policy::getACFailStatus();
496
497 bool powerStatusIPMI = false;
498 if (!checkIPMIRestartCause(ctx, powerStatusIPMI))
499 {
500 return ipmi::responseUnspecifiedError();
501 }
502
503 bool chassisIntrusionActive = false;
504 constexpr const char* chassisIntrusionObj =
505 "/xyz/openbmc_project/Chassis/Intrusion";
506 constexpr const char* chassisIntrusionInf =
507 "xyz.openbmc_project.Chassis.Intrusion";
508
509 std::string intrusionService;
510 boost::system::error_code ec = ipmi::getService(
511 ctx, chassisIntrusionInf, chassisIntrusionObj, intrusionService);
512 if (ec)
513 {
514 log<level::ERR>("Failed to get Chassis Intrusion service",
515 entry("ERROR=%s", ec.message().c_str()));
516 }
517
518 chassisIntrusionActive = !intrusionService.empty();
519
520 // This response has a lot of hard-coded, unsupported fields
521 // They are set to false or 0
522 constexpr bool powerOverload = false;
523 constexpr bool chassisInterlock = false;
524 constexpr bool powerFault = false;
525 constexpr bool powerControlFault = false;
526 constexpr bool powerDownOverload = false;
527 constexpr bool powerDownInterlock = false;
528 constexpr bool powerDownPowerFault = false;
529 constexpr bool frontPanelLockoutActive = false;
530 constexpr bool driveFault = false;
531 constexpr bool coolingFanFault = false;
532 // chassisIdentifySupport set because this command is implemented
533 constexpr bool chassisIdentifySupport = true;
534 uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
535 constexpr bool sleepButtonDisabled = false;
536 constexpr bool sleepButtonDisableAllow = false;
537
538 return ipmi::responseSuccess(
539 *powerGood, powerOverload, chassisInterlock, powerFault,
540 powerControlFault, *restorePolicy,
541 false, // reserved
542
543 powerDownAcFailed, powerDownOverload, powerDownInterlock,
544 powerDownPowerFault, powerStatusIPMI,
545 uint3_t(0), // reserved
546
547 chassisIntrusionActive, frontPanelLockoutActive, driveFault,
548 coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
549 false, // reserved
550
551 powerButtonDisabled, resetButtonDisabled, interruptButtonDisabled,
552 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
553 interruptButtonDisableAllow, sleepButtonDisableAllow);
554 }
555
getRestartCauseValue(const std::string & cause)556 static uint4_t getRestartCauseValue(const std::string& cause)
557 {
558 uint4_t restartCauseValue = 0;
559 if (cause == "xyz.openbmc_project.State.Host.RestartCause.Unknown")
560 {
561 restartCauseValue = 0x0;
562 }
563 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand")
564 {
565 restartCauseValue = 0x1;
566 }
567 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.ResetButton")
568 {
569 restartCauseValue = 0x2;
570 }
571 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.PowerButton")
572 {
573 restartCauseValue = 0x3;
574 }
575 else if (cause ==
576 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
577 {
578 restartCauseValue = 0x4;
579 }
580 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.OEM")
581 {
582 restartCauseValue = 0x5;
583 }
584 else if (cause ==
585 "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn")
586 {
587 restartCauseValue = 0x6;
588 }
589 else if (cause == "xyz.openbmc_project.State.Host.RestartCause."
590 "PowerPolicyPreviousState")
591 {
592 restartCauseValue = 0x7;
593 }
594 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.PEFReset")
595 {
596 restartCauseValue = 0x8;
597 }
598 else if (cause ==
599 "xyz.openbmc_project.State.Host.RestartCause.PEFPowerCycle")
600 {
601 restartCauseValue = 0x9;
602 }
603 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.SoftReset")
604 {
605 restartCauseValue = 0xa;
606 }
607 else if (cause == "xyz.openbmc_project.State.Host.RestartCause.RTCWakeup")
608 {
609 restartCauseValue = 0xb;
610 }
611 return restartCauseValue;
612 }
613
614 ipmi::RspType<uint4_t, // Restart Cause
615 uint4_t, // reserved
616 uint8_t // channel number
617 >
ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)618 ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
619 {
620 std::string restartCauseStr;
621 if (!getRestartCause(ctx, restartCauseStr))
622 {
623 return ipmi::responseUnspecifiedError();
624 }
625 constexpr uint4_t reserved = 0;
626 auto channel = static_cast<uint8_t>(ctx->channel);
627 return ipmi::responseSuccess(getRestartCauseValue(restartCauseStr),
628 reserved, channel);
629 }
630
ipmiSetFrontPanelButtonEnables(bool disablePowerButton,bool disableResetButton,bool disableInterruptButton,bool disableSleepButton,uint4_t reserved)631 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
632 bool disablePowerButton, bool disableResetButton,
633 bool disableInterruptButton, [[maybe_unused]] bool disableSleepButton,
634 uint4_t reserved)
635 {
636 if (reserved)
637 {
638 return ipmi::responseInvalidFieldRequest();
639 }
640 bool error = false;
641
642 error |= setButtonEnabled(powerButtonPath, disablePowerButton);
643 error |= setButtonEnabled(resetButtonPath, disableResetButton);
644 error |= setButtonEnabled(interruptButtonPath, disableInterruptButton);
645
646 if (error)
647 {
648 return ipmi::responseUnspecifiedError();
649 }
650
651 return ipmi::responseSuccess();
652 }
653
registerChassisFunctions(void)654 static void registerChassisFunctions(void)
655 {
656 log<level::INFO>("Registering Chassis commands");
657
658 createIdentifyTimer();
659
660 if (matchPtr == nullptr)
661 {
662 using namespace sdbusplus::bus::match::rules;
663 auto bus = getSdBus();
664
665 matchPtr = std::make_unique<sdbusplus::bus::match_t>(
666 *bus,
667 sdbusplus::bus::match::rules::propertiesChanged(idButtonPath,
668 buttonIntf),
669 std::bind(idButtonPropChanged, std::placeholders::_1));
670 }
671
672 // <Chassis Identify>
673 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
674 ipmi::chassis::cmdChassisIdentify,
675 ipmi::Privilege::Operator, ipmiChassisIdentify);
676 // <Get Chassis Status>
677 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
678 ipmi::chassis::cmdGetChassisStatus,
679 ipmi::Privilege::User, ipmiGetChassisStatus);
680 // <Get System Restart Cause>
681 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
682 ipmi::chassis::cmdGetSystemRestartCause,
683 ipmi::Privilege::User, ipmiGetSystemRestartCause);
684 // <Set Front Panel Enables>
685 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
686 ipmi::chassis::cmdSetFrontPanelButtonEnables,
687 ipmi::Privilege::Admin,
688 ipmiSetFrontPanelButtonEnables);
689 }
690
691 } // namespace ipmi::chassis
692