blob: 9c379390374d899ad5288d1391f7710cf84d384b [file] [log] [blame]
Yuan Li60e7aaf2019-05-28 14:22:40 +08001/*
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 <fstream>
Jason M. Billsb08f84e2019-06-10 12:59:42 -070019#include <iostream>
Yuan Li60e7aaf2019-05-28 14:22:40 +080020#include <ipmid/api.hpp>
21#include <ipmid/utils.hpp>
22#include <nlohmann/json.hpp>
23#include <phosphor-logging/elog-errors.hpp>
Jason M. Billsb08f84e2019-06-10 12:59:42 -070024#include <phosphor-logging/log.hpp>
Yuan Li60e7aaf2019-05-28 14:22:40 +080025#include <regex>
26#include <sdbusplus/timer.hpp>
Jason M. Billsb08f84e2019-06-10 12:59:42 -070027#include <stdexcept>
28#include <string_view>
29#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
Yuan Li60e7aaf2019-05-28 14:22:40 +080030
Jason M. Billsb08f84e2019-06-10 12:59:42 -070031using namespace phosphor::logging;
32
33namespace ipmi::chassis
Yuan Li60e7aaf2019-05-28 14:22:40 +080034{
Jason M. Billsb08f84e2019-06-10 12:59:42 -070035static constexpr const char* buttonIntf = "xyz.openbmc_project.Chassis.Buttons";
Yuan Li60e7aaf2019-05-28 14:22:40 +080036
Jason M. Billsb08f84e2019-06-10 12:59:42 -070037const static constexpr char* idButtonPath =
38 "/xyz/openbmc_project/chassis/buttons/id";
39static constexpr const char* powerButtonPath =
40 "/xyz/openbmc_project/chassis/buttons/power";
41static constexpr const char* resetButtonPath =
42 "/xyz/openbmc_project/chassis/buttons/reset";
43static constexpr const char* interruptButtonPath =
44 "/xyz/openbmc_project/chassis/buttons/nmi";
45
46const static constexpr char* idButtonProp = "ButtonPressed";
47
48const static constexpr char* ledService =
Yuan Li60e7aaf2019-05-28 14:22:40 +080049 "xyz.openbmc_project.LED.GroupManager";
Jason M. Billsb08f84e2019-06-10 12:59:42 -070050const static constexpr char* ledIDOnObj =
Yuan Li60e7aaf2019-05-28 14:22:40 +080051 "/xyz/openbmc_project/led/groups/enclosure_identify";
Jason M. Billsb08f84e2019-06-10 12:59:42 -070052const static constexpr char* ledIDBlinkObj =
Yuan Li60e7aaf2019-05-28 14:22:40 +080053 "/xyz/openbmc_project/led/groups/enclosure_identify_blink";
Jason M. Billsb08f84e2019-06-10 12:59:42 -070054const static constexpr char* ledInterface = "xyz.openbmc_project.Led.Group";
55const static constexpr char* ledProp = "Asserted";
Yuan Li60e7aaf2019-05-28 14:22:40 +080056
57constexpr size_t defaultIdentifyTimeOut = 15;
58
59std::unique_ptr<phosphor::Timer> identifyTimer
60 __attribute__((init_priority(101)));
61std::unique_ptr<sdbusplus::bus::match_t> matchPtr
62 __attribute__((init_priority(101)));
63
64static void registerChassisFunctions() __attribute__((constructor));
65
66static ipmi::ServiceCache LEDService(ledInterface, ledIDBlinkObj);
67
Jason M. Billsb08f84e2019-06-10 12:59:42 -070068void enclosureIdentifyLed(const char* objName, bool isIdLedOn)
Yuan Li60e7aaf2019-05-28 14:22:40 +080069{
70 auto bus = getSdBus();
71
72 try
73 {
74 std::string service = LEDService.getService(*bus);
75 setDbusProperty(*bus, service, objName, ledInterface, ledProp,
76 isIdLedOn);
77 }
Jason M. Billsb08f84e2019-06-10 12:59:42 -070078 catch (const std::exception& e)
Yuan Li60e7aaf2019-05-28 14:22:40 +080079 {
Jason M. Billsb08f84e2019-06-10 12:59:42 -070080 log<level::ERR>("enclosureIdentifyLed: can't set property",
81 entry("ERR=%s", e.what()));
Yuan Li60e7aaf2019-05-28 14:22:40 +080082 }
83}
84
Jason M. Billsb08f84e2019-06-10 12:59:42 -070085bool getIDState(const char* objName, bool& state)
Yuan Li60e7aaf2019-05-28 14:22:40 +080086{
87 auto bus = getSdBus();
88
89 try
90 {
91 std::string service = LEDService.getService(*bus);
92 ipmi::Value enabled =
93 getDbusProperty(*bus, service, objName, ledInterface, ledProp);
94 state = std::get<bool>(enabled);
95 }
Jason M. Billsb08f84e2019-06-10 12:59:42 -070096 catch (sdbusplus::exception::SdBusError& e)
Yuan Li60e7aaf2019-05-28 14:22:40 +080097 {
Jason M. Billsb08f84e2019-06-10 12:59:42 -070098 log<level::ERR>("Fail to get property", entry("PATH=%s", objName),
99 entry("ERROR=%s", e.what()));
Yuan Li60e7aaf2019-05-28 14:22:40 +0800100 return false;
101 }
102 return true;
103}
104
105void enclosureIdentifyLedBlinkOff()
106{
107 enclosureIdentifyLed(ledIDBlinkObj, false);
108}
109
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700110void idButtonPropChanged(sdbusplus::message::message& msg)
Yuan Li60e7aaf2019-05-28 14:22:40 +0800111{
112 bool asserted = false;
113 bool buttonPressed = false;
114
115 std::map<std::string, ipmi::Value> props;
116 std::vector<std::string> inval;
117 std::string iface;
118 msg.read(iface, props, inval);
119
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700120 for (const auto& t : props)
Yuan Li60e7aaf2019-05-28 14:22:40 +0800121 {
122 auto key = t.first;
123 auto value = t.second;
124
125 if (key == idButtonProp)
126 {
127 buttonPressed = std::get<bool>(value);
128 }
129 break;
130 }
131
132 if (buttonPressed)
133 {
134 if (identifyTimer->isRunning())
135 {
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700136 log<level::INFO>("ID timer is running");
Yuan Li60e7aaf2019-05-28 14:22:40 +0800137 }
138
139 // make sure timer is stopped
140 identifyTimer->stop();
141
142 if (!getIDState(ledIDBlinkObj, asserted))
143 {
144 return;
145 }
146
147 if (asserted)
148 {
149 // LED is blinking, turn off the LED
150 enclosureIdentifyLed(ledIDBlinkObj, false);
151 enclosureIdentifyLed(ledIDOnObj, false);
152 }
153 else
154 {
155 // toggle the IED on/off
156 if (!getIDState(ledIDOnObj, asserted))
157 {
158 return;
159 }
160 enclosureIdentifyLed(ledIDOnObj, !asserted);
161 }
162 }
163}
164
165void createIdentifyTimer()
166{
167 if (!identifyTimer)
168 {
169 identifyTimer =
170 std::make_unique<phosphor::Timer>(enclosureIdentifyLedBlinkOff);
171 }
172}
173
174ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
175 std::optional<uint8_t> force)
176{
177 uint8_t identifyInterval = interval.value_or(defaultIdentifyTimeOut);
178 bool forceIdentify = force.value_or(0) & 0x01;
179
180 enclosureIdentifyLed(ledIDOnObj, false);
181 identifyTimer->stop();
182
183 if (identifyInterval || forceIdentify)
184 {
185 enclosureIdentifyLed(ledIDBlinkObj, true);
186 if (forceIdentify)
187 {
188 return ipmi::responseSuccess();
189 }
190 // start the timer
191 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
192 std::chrono::seconds(identifyInterval));
193 identifyTimer->start(time);
194 }
195 else
196 {
197 enclosureIdentifyLed(ledIDBlinkObj, false);
198 }
199 return ipmi::responseSuccess();
200}
201
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700202namespace power_policy
203{
204/* helper function for Get Chassis Status Command
205 */
206std::optional<uint2_t> getPowerRestorePolicy()
207{
208 constexpr const char* powerRestorePath =
209 "/xyz/openbmc_project/control/host0/power_restore_policy";
210 constexpr const char* powerRestoreIntf =
211 "xyz.openbmc_project.Control.Power.RestorePolicy";
212 uint2_t restorePolicy = 0;
213 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
214
215 try
216 {
217 auto service =
218 ipmi::getService(*busp, powerRestoreIntf, powerRestorePath);
219
220 ipmi::Value result =
221 ipmi::getDbusProperty(*busp, service, powerRestorePath,
222 powerRestoreIntf, "PowerRestorePolicy");
223 auto powerRestore = sdbusplus::xyz::openbmc_project::Control::Power::
224 server::RestorePolicy::convertPolicyFromString(
225 std::get<std::string>(result));
226
227 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
228 switch (powerRestore)
229 {
230 case RestorePolicy::Policy::AlwaysOff:
231 restorePolicy = 0x00;
232 break;
233 case RestorePolicy::Policy::Restore:
234 restorePolicy = 0x01;
235 break;
236 case RestorePolicy::Policy::AlwaysOn:
237 restorePolicy = 0x02;
238 break;
239 }
240 }
241 catch (const std::exception& e)
242 {
243 log<level::ERR>("Failed to fetch PowerRestorePolicy property",
244 entry("ERROR=%s", e.what()),
245 entry("PATH=%s", powerRestorePath),
246 entry("INTERFACE=%s", powerRestoreIntf));
247 return std::nullopt;
248 }
249 return std::make_optional(restorePolicy);
250}
251
252/*
253 * getPowerStatus
254 * helper function for Get Chassis Status Command
255 * return - optional value for pgood (no value on error)
256 */
257std::optional<bool> getPowerStatus()
258{
259 bool powerGood = false;
260 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
261 try
262 {
263 constexpr const char* chassisStatePath =
264 "/xyz/openbmc_project/state/chassis0";
265 constexpr const char* chassisStateIntf =
266 "xyz.openbmc_project.State.Chassis";
267 auto service =
268 ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
269
270 ipmi::Value variant =
271 ipmi::getDbusProperty(*busp, service, chassisStatePath,
272 chassisStateIntf, "CurrentPowerState");
273 std::string powerState = std::get<std::string>(variant);
274 if (powerState == "xyz.openbmc_project.State.Chassis.PowerState.On")
275 {
276 powerGood = true;
277 }
278 }
279 catch (const std::exception& e)
280 {
281 log<level::ERR>("Failed to fetch power state property",
282 entry("ERROR=%s", e.what()));
283 return std::nullopt;
284 }
285 return std::make_optional(powerGood);
286}
287
288/*
289 * getACFailStatus
290 * helper function for Get Chassis Status Command
291 * return - bool value for ACFail (false on error)
292 */
293bool getACFailStatus()
294{
Jason M. Billsa2b4c7a2019-06-24 16:55:21 -0700295 constexpr const char* acBootObj =
296 "/xyz/openbmc_project/control/host0/ac_boot";
297 constexpr const char* acBootIntf = "xyz.openbmc_project.Common.ACBoot";
298 std::string acFail;
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700299 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
300 try
301 {
Jason M. Billsa2b4c7a2019-06-24 16:55:21 -0700302 auto service = ipmi::getService(*bus, acBootIntf, acBootObj);
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700303
Jason M. Billsa2b4c7a2019-06-24 16:55:21 -0700304 ipmi::Value variant = ipmi::getDbusProperty(*bus, service, acBootObj,
305 acBootIntf, "ACBoot");
306 acFail = std::get<std::string>(variant);
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700307 }
308 catch (const std::exception& e)
309 {
Jason M. Billsa2b4c7a2019-06-24 16:55:21 -0700310 log<level::ERR>(
311 "Failed to fetch ACBoot property", entry("ERROR=%s", e.what()),
312 entry("PATH=%s", acBootObj), entry("INTERFACE=%s", acBootIntf));
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700313 }
Jason M. Billsa2b4c7a2019-06-24 16:55:21 -0700314 return acFail == "True";
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700315}
316} // namespace power_policy
317
318static std::optional<bool> getButtonEnabled(const std::string& buttonPath)
319{
320 bool buttonDisabled = false;
321 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
322 try
323 {
324 auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
325 ipmi::Value disabled = ipmi::getDbusProperty(
326 *busp, service, buttonPath, buttonIntf, "ButtonMasked");
327 buttonDisabled = std::get<bool>(disabled);
328 }
329 catch (sdbusplus::exception::SdBusError& e)
330 {
331 log<level::ERR>("Fail to get button disabled property",
332 entry("PATH=%s", buttonPath.c_str()),
333 entry("ERROR=%s", e.what()));
334 return std::nullopt;
335 }
336 return std::make_optional(buttonDisabled);
337}
338
339static bool setButtonEnabled(const std::string& buttonPath, const bool disabled)
340{
341 try
342 {
343 auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
344 ipmi::setDbusProperty(*getSdBus(), service, buttonPath, buttonIntf,
345 "ButtonMasked", disabled);
346 }
347 catch (std::exception& e)
348 {
349 log<level::ERR>("Failed to set button disabled",
350 entry("EXCEPTION=%s, REQUEST=%x", e.what(), disabled));
351 return -1;
352 }
353
354 return 0;
355}
356
357//----------------------------------------------------------------------
358// Get Chassis Status commands
359//----------------------------------------------------------------------
360ipmi::RspType<bool, // Power is on
361 bool, // Power overload
362 bool, // Interlock
363 bool, // power fault
364 bool, // power control fault
365 uint2_t, // power restore policy
366 bool, // reserved
367
368 bool, // AC failed
369 bool, // last power down caused by a Power overload
370 bool, // last power down caused by a power interlock
371 bool, // last power down caused by power fault
372 bool, // last ‘Power is on’ state was entered via IPMI command
373 uint3_t, // reserved
374
375 bool, // Chassis intrusion active
376 bool, // Front Panel Lockout active
377 bool, // Drive Fault
378 bool, // Cooling/fan fault detected
379 uint2_t, // Chassis Identify State
380 bool, // Chassis Identify command and state info supported
381 bool, // reserved
382
383 bool, // Power off button disabled
384 bool, // Reset button disabled
385 bool, // Diagnostic Interrupt button disabled
386 bool, // Standby (sleep) button disabled
387 bool, // Power off button disable allowed
388 bool, // Reset button disable allowed
389 bool, // Diagnostic Interrupt button disable allowed
390 bool // Standby (sleep) button disable allowed
391 >
392 ipmiGetChassisStatus()
393{
394 std::optional<uint2_t> restorePolicy =
395 power_policy::getPowerRestorePolicy();
396 std::optional<bool> powerGood = power_policy::getPowerStatus();
397 if (!restorePolicy || !powerGood)
398 {
399 return ipmi::responseUnspecifiedError();
400 }
401
402 // Front Panel Button Capabilities and disable/enable status(Optional)
403 std::optional<bool> powerButtonReading = getButtonEnabled(powerButtonPath);
404 // allow disable if the interface is present
405 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
406 // default return the button is enabled (not disabled)
407 bool powerButtonDisabled = false;
408 if (powerButtonDisableAllow)
409 {
410 // return the real value of the button status, if present
411 powerButtonDisabled = *powerButtonReading;
412 }
413
414 std::optional<bool> resetButtonReading = getButtonEnabled(resetButtonPath);
415 // allow disable if the interface is present
416 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
417 // default return the button is enabled (not disabled)
418 bool resetButtonDisabled = false;
419 if (resetButtonDisableAllow)
420 {
421 // return the real value of the button status, if present
422 resetButtonDisabled = *resetButtonReading;
423 }
424
425 std::optional<bool> interruptButtonReading =
426 getButtonEnabled(interruptButtonPath);
427 // allow disable if the interface is present
428 bool interruptButtonDisableAllow =
429 static_cast<bool>(interruptButtonReading);
430 // default return the button is enabled (not disabled)
431 bool interruptButtonDisabled = false;
432 if (interruptButtonDisableAllow)
433 {
434 // return the real value of the button status, if present
435 interruptButtonDisabled = *interruptButtonReading;
436 }
437
438 bool powerDownAcFailed = power_policy::getACFailStatus();
439
440 // This response has a lot of hard-coded, unsupported fields
441 // They are set to false or 0
442 constexpr bool powerOverload = false;
443 constexpr bool chassisInterlock = false;
444 constexpr bool powerFault = false;
445 constexpr bool powerControlFault = false;
446 constexpr bool powerDownOverload = false;
447 constexpr bool powerDownInterlock = false;
448 constexpr bool powerDownPowerFault = false;
449 constexpr bool powerStatusIPMI = false;
450 constexpr bool chassisIntrusionActive = false;
451 constexpr bool frontPanelLockoutActive = false;
452 constexpr bool driveFault = false;
453 constexpr bool coolingFanFault = false;
454 // chassisIdentifySupport set because this command is implemented
455 constexpr bool chassisIdentifySupport = true;
456 uint2_t chassisIdentifyState = 0;
457 constexpr bool sleepButtonDisabled = false;
458 constexpr bool sleepButtonDisableAllow = false;
459
460 return ipmi::responseSuccess(
461 *powerGood, powerOverload, chassisInterlock, powerFault,
462 powerControlFault, *restorePolicy,
463 false, // reserved
464
465 powerDownAcFailed, powerDownOverload, powerDownInterlock,
466 powerDownPowerFault, powerStatusIPMI,
467 uint3_t(0), // reserved
468
469 chassisIntrusionActive, frontPanelLockoutActive, driveFault,
470 coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
471 false, // reserved
472
473 powerButtonDisabled, resetButtonDisabled, interruptButtonDisabled,
474 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
475 interruptButtonDisableAllow, sleepButtonDisableAllow);
476}
477
478ipmi::RspType<> ipmiSetFrontPanelButtonEnables(bool disablePowerButton,
479 bool disableResetButton,
480 bool disableInterruptButton,
481 bool disableSleepButton,
482 uint4_t reserved)
483{
484 bool error = false;
485
486 error |= setButtonEnabled(powerButtonPath, disablePowerButton);
487 error |= setButtonEnabled(resetButtonPath, disableResetButton);
488 error |= setButtonEnabled(interruptButtonPath, disableInterruptButton);
489
490 if (error)
491 {
492 return ipmi::responseUnspecifiedError();
493 }
494 return ipmi::responseSuccess();
495}
496
Yuan Li60e7aaf2019-05-28 14:22:40 +0800497static void registerChassisFunctions(void)
498{
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700499 log<level::INFO>("Registering Chassis commands");
Yuan Li60e7aaf2019-05-28 14:22:40 +0800500
501 createIdentifyTimer();
502
503 if (matchPtr == nullptr)
504 {
505 using namespace sdbusplus::bus::match::rules;
506 auto bus = getSdBus();
507
508 matchPtr = std::make_unique<sdbusplus::bus::match_t>(
509 *bus,
510 sdbusplus::bus::match::rules::propertiesChanged(idButtonPath,
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700511 buttonIntf),
Yuan Li60e7aaf2019-05-28 14:22:40 +0800512 std::bind(idButtonPropChanged, std::placeholders::_1));
513 }
514
515 // <Chassis Identify>
516 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
517 ipmi::chassis::cmdChassisIdentify,
518 ipmi::Privilege::Operator, ipmiChassisIdentify);
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700519 // <Get Chassis Status>
520 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
521 ipmi::chassis::cmdGetChassisStatus,
522 ipmi::Privilege::User, ipmiGetChassisStatus);
523 // <Set Front Panel Enables>
524 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
525 ipmi::chassis::cmdSetFrontPanelButtonEnables,
526 ipmi::Privilege::User,
527 ipmiSetFrontPanelButtonEnables);
Yuan Li60e7aaf2019-05-28 14:22:40 +0800528}
529
Jason M. Billsb08f84e2019-06-10 12:59:42 -0700530} // namespace ipmi::chassis