/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include #include #include namespace ipmi { Manufacturing mtm; static auto revertTimeOut = std::chrono::duration_cast( std::chrono::seconds(60)); // 1 minute timeout static constexpr const char* callbackMgrService = "xyz.openbmc_project.CallbackManager"; static constexpr const char* callbackMgrIntf = "xyz.openbmc_project.CallbackManager"; static constexpr const char* callbackMgrObjPath = "/xyz/openbmc_project/CallbackManager"; static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate"; const static constexpr char* systemDService = "org.freedesktop.systemd1"; const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1"; const static constexpr char* systemDMgrIntf = "org.freedesktop.systemd1.Manager"; const static constexpr char* pidControlService = "phosphor-pid-control.service"; int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path) { switch (signal) { case SmSignalGet::smPowerButton: path = "/xyz/openbmc_project/chassis/buttons/power"; break; case SmSignalGet::smResetButton: path = "/xyz/openbmc_project/chassis/buttons/reset"; break; case SmSignalGet::smNMIButton: path = "/xyz/openbmc_project/chassis/buttons/nmi"; break; case SmSignalGet::smIdentifyButton: path = "/xyz/openbmc_project/chassis/buttons/id"; break; default: return -1; break; } return 0; } ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState) { LedProperty* ledProp = mtm.findLedProperty(signal); if (ledProp == nullptr) { return IPMI_CC_INVALID_FIELD_REQUEST; } std::string ledName = ledProp->getName(); std::string ledService = ledServicePrefix + ledName; std::string ledPath = ledPathPrefix + ledName; ipmi::Value presentState; if (false == ledProp->getLock()) { if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf, "State", &presentState) != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } ledProp->setPrevState(std::get(presentState)); ledProp->setLock(true); if (signal == SmSignalSet::smPowerFaultLed || signal == SmSignalSet::smSystemReadyLed) { mtm.revertLedCallback = true; } } if (mtm.setProperty(ledService, ledPath, ledIntf, "State", ledStateStr + setState) != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } return IPMI_CC_OK; } ipmi_ret_t ledRevert(SmSignalSet signal) { LedProperty* ledProp = mtm.findLedProperty(signal); if (ledProp == nullptr) { return IPMI_CC_INVALID_FIELD_REQUEST; } if (true == ledProp->getLock()) { ledProp->setLock(false); if (signal == SmSignalSet::smPowerFaultLed || signal == SmSignalSet::smSystemReadyLed) { try { ipmi::method_no_args::callDbusMethod( *getSdBus(), callbackMgrService, callbackMgrObjPath, callbackMgrIntf, retriggerLedUpdate); } catch (sdbusplus::exception_t& e) { return IPMI_CC_UNSPECIFIED_ERROR; } mtm.revertLedCallback = false; } else { std::string ledName = ledProp->getName(); std::string ledService = ledServicePrefix + ledName; std::string ledPath = ledPathPrefix + ledName; if (mtm.setProperty(ledService, ledPath, ledIntf, "State", ledProp->getPrevState()) != 0) { return IPMI_CC_UNSPECIFIED_ERROR; } } } return IPMI_CC_OK; } void Manufacturing::initData() { ledPropertyList.push_back( LedProperty(SmSignalSet::smPowerFaultLed, "status_amber")); ledPropertyList.push_back( LedProperty(SmSignalSet::smSystemReadyLed, "status_green")); ledPropertyList.push_back( LedProperty(SmSignalSet::smIdentifyLed, "identify")); } void Manufacturing::revertTimerHandler() { if (revertFanPWM) { revertFanPWM = false; disablePidControlService(false); } for (const auto& ledProperty : ledPropertyList) { const std::string& ledName = ledProperty.getName(); ledRevert(ledProperty.getSignal()); } } Manufacturing::Manufacturing() : revertTimer([&](void) { revertTimerHandler(); }) { initData(); } int8_t Manufacturing::getProperty(const std::string& service, const std::string& path, const std::string& interface, const std::string& propertyName, ipmi::Value* reply) { try { *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface, propertyName); } catch (const sdbusplus::exception::SdBusError& e) { phosphor::logging::log( "ERROR: getProperty"); return -1; } return 0; } int8_t Manufacturing::setProperty(const std::string& service, const std::string& path, const std::string& interface, const std::string& propertyName, ipmi::Value value) { try { ipmi::setDbusProperty(*getSdBus(), service, path, interface, propertyName, value); } catch (const sdbusplus::exception::SdBusError& e) { phosphor::logging::log( "ERROR: setProperty"); return -1; } return 0; } int8_t Manufacturing::disablePidControlService(const bool disable) { try { auto dbus = getSdBus(); auto method = dbus->new_method_call(systemDService, systemDObjPath, systemDMgrIntf, disable ? "StopUnit" : "StartUnit"); method.append(pidControlService, "replace"); auto reply = dbus->call(method); } catch (const sdbusplus::exception::SdBusError& e) { phosphor::logging::log( "ERROR: phosphor-pid-control service start or stop failed"); return -1; } return 0; } ipmi::RspType // Fan tach value > appMTMGetSignal(uint8_t signalTypeByte, uint8_t instance, uint8_t actionByte) { if (mtm.getAccessLvl() < MtmLvl::mtmAvailable) { return ipmi::responseInvalidCommand(); } SmSignalGet signalType = static_cast(signalTypeByte); SmActionGet action = static_cast(actionByte); switch (signalType) { case SmSignalGet::smFanPwmGet: { ipmi::Value reply; std::string fullPath = fanPwmPath + std::to_string(instance); if (mtm.getProperty(fanService, fullPath, fanIntf, "Value", &reply) < 0) { return ipmi::responseInvalidFieldRequest(); } double* doubleVal = std::get_if(&reply); if (doubleVal == nullptr) { return ipmi::responseUnspecifiedError(); } uint8_t sensorVal = std::round(*doubleVal); return ipmi::responseSuccess(sensorVal, std::nullopt); } break; case SmSignalGet::smFanTachometerGet: { // Full path calculation pattern: // Instance 1 path is // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3 // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a // and so on... std::string fullPath = fanTachPathPrefix; std::string fanAb = (instance % 2) == 0 ? "b" : "a"; if (0 == instance) { return ipmi::responseInvalidFieldRequest(); } else if (0 == instance / 2) { fullPath += std::string("1") + fanAb; } else { fullPath += std::to_string(instance / 2) + fanAb; } ipmi::Value reply; if (mtm.getProperty(fanService, fullPath, fanIntf, "Value", &reply) < 0) { return ipmi::responseInvalidFieldRequest(); } double* doubleVal = std::get_if(&reply); if (doubleVal == nullptr) { return ipmi::responseUnspecifiedError(); } uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT; std::optional fanTach = std::round(*doubleVal); return ipmi::responseSuccess(sensorVal, fanTach); } break; case SmSignalGet::smIdentifyButton: { if (action == SmActionGet::revert || action == SmActionGet::ignore) { // ButtonMasked property is not supported for ID button as it is // unnecessary. Hence if requested for revert / ignore, override // it to sample action to make tools happy. action = SmActionGet::sample; } // fall-through } case SmSignalGet::smResetButton: case SmSignalGet::smPowerButton: case SmSignalGet::smNMIButton: { std::string path; if (getGpioPathForSmSignal(signalType, path) < 0) { return ipmi::responseInvalidFieldRequest(); } switch (action) { case SmActionGet::sample: phosphor::logging::log( "case SmActionGet::sample"); break; case SmActionGet::ignore: { phosphor::logging::log( "case SmActionGet::ignore"); if (mtm.setProperty(buttonService, path, buttonIntf, "ButtonMasked", true) < 0) { return ipmi::responseUnspecifiedError(); } } break; case SmActionGet::revert: { phosphor::logging::log( "case SmActionGet::revert"); if (mtm.setProperty(buttonService, path, buttonIntf, "ButtonMasked", false) < 0) { return ipmi::responseUnspecifiedError(); } } break; default: return ipmi::responseInvalidFieldRequest(); break; } ipmi::Value reply; if (mtm.getProperty(buttonService, path, buttonIntf, "ButtonPressed", &reply) < 0) { return ipmi::responseUnspecifiedError(); } bool* valPtr = std::get_if(&reply); if (valPtr == nullptr) { return ipmi::responseUnspecifiedError(); } uint8_t sensorVal = *valPtr; return ipmi::responseSuccess(sensorVal, std::nullopt); } break; default: return ipmi::responseInvalidFieldRequest(); break; } } ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { uint8_t ret = 0; ipmi_ret_t retCode = IPMI_CC_OK; SetSmSignalReq* pReq = static_cast(request); std::string ledName; /////////////////// Signal to led configuration //////////////// // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148 // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149 // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150 // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216 ///////////////////////////////////////////////////////////////// if ((*data_len == sizeof(*pReq)) && (mtm.getAccessLvl() >= MtmLvl::mtmAvailable)) { switch (pReq->Signal) { case SmSignalSet::smPowerFaultLed: case SmSignalSet::smSystemReadyLed: case SmSignalSet::smIdentifyLed: switch (pReq->Action) { case SmActionSet::forceDeasserted: { phosphor::logging::log( "case SmActionSet::forceDeasserted"); retCode = ledStoreAndSet(pReq->Signal, std::string("Off")); if (retCode != IPMI_CC_OK) { break; } mtm.revertTimer.start(revertTimeOut); } break; case SmActionSet::forceAsserted: { phosphor::logging::log( "case SmActionSet::forceAsserted"); retCode = ledStoreAndSet(pReq->Signal, std::string("On")); if (retCode != IPMI_CC_OK) { break; } mtm.revertTimer.start(revertTimeOut); if (SmSignalSet::smPowerFaultLed == pReq->Signal) { // Deassert "system ready" retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed, std::string("Off")); if (retCode != IPMI_CC_OK) { break; } } else if (SmSignalSet::smSystemReadyLed == pReq->Signal) { // Deassert "fault led" retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed, std::string("Off")); if (retCode != IPMI_CC_OK) { break; } } } break; case SmActionSet::revert: { phosphor::logging::log( "case SmActionSet::revert"); retCode = ledRevert(pReq->Signal); if (retCode != IPMI_CC_OK) { break; } } break; default: { retCode = IPMI_CC_INVALID_FIELD_REQUEST; } break; } break; case SmSignalSet::smFanPowerSpeed: { if (((pReq->Action == SmActionSet::forceAsserted) && (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) || pReq->Instance == 0) { retCode = IPMI_CC_INVALID_FIELD_REQUEST; break; } uint8_t pwmValue = 0; switch (pReq->Action) { case SmActionSet::revert: { if (mtm.revertFanPWM) { ret = mtm.disablePidControlService(false); if (ret < 0) { retCode = IPMI_CC_UNSPECIFIED_ERROR; break; } mtm.revertFanPWM = false; } } break; case SmActionSet::forceAsserted: { pwmValue = pReq->Value; } // fall-through case SmActionSet::forceDeasserted: { if (!mtm.revertFanPWM) { ret = mtm.disablePidControlService(true); if (ret < 0) { retCode = IPMI_CC_UNSPECIFIED_ERROR; break; } mtm.revertFanPWM = true; } mtm.revertTimer.start(revertTimeOut); std::string fanPwmInstancePath = fanPwmPath + std::to_string(pReq->Instance); ret = mtm.setProperty(fanService, fanPwmInstancePath, fanIntf, "Value", static_cast(pwmValue)); if (ret < 0) { retCode = IPMI_CC_UNSPECIFIED_ERROR; } } break; default: { retCode = IPMI_CC_INVALID_FIELD_REQUEST; } break; } } break; default: { retCode = IPMI_CC_INVALID_FIELD_REQUEST; } break; } } else { retCode = IPMI_CC_INVALID; } *data_len = 0; // Only CC is return for SetSmSignal cmd return retCode; } } // namespace ipmi void register_mtm_commands() __attribute__((constructor)); void register_mtm_commands() { // ipmi::registerHandler( ipmi::prioOemBase, ipmi::netFnOemOne, static_cast(IPMINetFnIntelOemGeneralCmds::GetSmSignal), ipmi::Privilege::User, ipmi::appMTMGetSignal); ipmi_register_callback( netfnIntcOEMGeneral, static_cast(IPMINetFnIntelOemGeneralCmds::SetSmSignal), NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER); return; }