1 /* 2 // Copyright (c) 2018 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 17 #include "oemcommands.hpp" 18 19 #include <boost/algorithm/string.hpp> 20 #include <ipmid/api.hpp> 21 #include <ipmid/utils.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <variant> 24 25 namespace ipmi 26 { 27 void register_netfn_bmc_control_functions() __attribute__((constructor)); 28 29 static constexpr uint8_t rmcpServiceBitPos = 3; 30 static constexpr uint8_t webServiceBitPos = 5; 31 static constexpr uint8_t solServiceBitPos = 6; 32 static constexpr uint8_t kvmServiceBitPos = 15; 33 34 static const std::unordered_map<uint8_t, std::string> bmcServices = { 35 // {bit position for service, service object path} 36 {rmcpServiceBitPos, 37 "/xyz/openbmc_project/control/service/phosphor_2dipmi_2dnet"}, 38 {webServiceBitPos, "/xyz/openbmc_project/control/service/bmcweb"}, 39 {solServiceBitPos, "/xyz/openbmc_project/control/service/obmc_2dconsole"}, 40 {kvmServiceBitPos, "/xyz/openbmc_project/control/service/start_2dipkvm"}, 41 }; 42 43 static constexpr uint16_t maskBit15 = 0xF000; 44 45 static constexpr const char* objectManagerIntf = 46 "org.freedesktop.DBus.ObjectManager"; 47 static constexpr const char* dBusPropIntf = "org.freedesktop.DBus.Properties"; 48 static constexpr const char* serviceConfigBasePath = 49 "/xyz/openbmc_project/control/service"; 50 static constexpr const char* serviceConfigAttrIntf = 51 "xyz.openbmc_project.Control.Service.Attributes"; 52 static constexpr const char* getMgdObjMethod = "GetManagedObjects"; 53 static constexpr const char* propMasked = "Masked"; 54 55 std::string getServiceConfigMgrName() 56 { 57 static std::string serviceCfgMgr{}; 58 if (serviceCfgMgr.empty()) 59 { 60 try 61 { 62 auto sdbusp = getSdBus(); 63 serviceCfgMgr = ipmi::getService(*sdbusp, objectManagerIntf, 64 serviceConfigBasePath); 65 } 66 catch (const sdbusplus::exception::SdBusError& e) 67 { 68 serviceCfgMgr.clear(); 69 phosphor::logging::log<phosphor::logging::level::ERR>( 70 "Error: In fetching disabling service manager name"); 71 return serviceCfgMgr; 72 } 73 } 74 return serviceCfgMgr; 75 } 76 77 static inline void checkAndThrowError(boost::system::error_code& ec, 78 const std::string& msg) 79 { 80 if (ec) 81 { 82 std::string msgToLog = ec.message() + (msg.empty() ? "" : " - " + msg); 83 phosphor::logging::log<phosphor::logging::level::ERR>(msgToLog.c_str()); 84 throw sdbusplus::exception::SdBusError(-EIO, msgToLog.c_str()); 85 } 86 return; 87 } 88 89 static inline bool getEnabledValue(const DbusInterfaceMap& intfMap) 90 { 91 for (const auto& intf : intfMap) 92 { 93 if (intf.first == serviceConfigAttrIntf) 94 { 95 auto it = intf.second.find(propMasked); 96 if (it == intf.second.end()) 97 { 98 phosphor::logging::log<phosphor::logging::level::ERR>( 99 "Error: in getting Masked property value"); 100 throw sdbusplus::exception::SdBusError( 101 -EIO, "ERROR in reading Masked property value"); 102 } 103 // return !Masked value 104 return !std::get<bool>(it->second); 105 } 106 } 107 return false; 108 } 109 110 ipmi::RspType<> setBmcControlServices(boost::asio::yield_context yield, 111 uint8_t state, uint16_t serviceValue) 112 { 113 constexpr uint16_t servicesRsvdMask = 0x3F97; 114 constexpr uint8_t enableService = 0x1; 115 116 if ((state > enableService) || (serviceValue & servicesRsvdMask) || 117 !serviceValue) 118 { 119 return ipmi::responseInvalidFieldRequest(); 120 } 121 try 122 { 123 auto sdbusp = getSdBus(); 124 boost::system::error_code ec; 125 auto objectMap = sdbusp->yield_method_call<ObjectValueTree>( 126 yield, ec, getServiceConfigMgrName().c_str(), serviceConfigBasePath, 127 objectManagerIntf, getMgdObjMethod); 128 checkAndThrowError(ec, "GetMangagedObjects for service cfg failed"); 129 130 for (const auto& services : bmcServices) 131 { 132 // services.first holds the bit position of the service, check 133 // whether it has to be updated. 134 const uint16_t serviceMask = 1 << services.first; 135 if (!(serviceValue & serviceMask)) 136 { 137 continue; 138 } 139 for (const auto& obj : objectMap) 140 { 141 if (boost::algorithm::starts_with(obj.first.str, 142 services.second)) 143 { 144 if (state != getEnabledValue(obj.second)) 145 { 146 ec.clear(); 147 sdbusp->yield_method_call<>( 148 yield, ec, getServiceConfigMgrName().c_str(), 149 obj.first.str, dBusPropIntf, "Set", 150 serviceConfigAttrIntf, propMasked, 151 std::variant<bool>(!state)); 152 checkAndThrowError(ec, "Set Masked property failed"); 153 // Multiple instances may be present, so continue 154 } 155 } 156 } 157 } 158 } 159 catch (sdbusplus::exception::SdBusError& e) 160 { 161 return ipmi::responseUnspecifiedError(); 162 } 163 return ipmi::responseSuccess(); 164 } 165 166 ipmi::RspType<uint16_t> getBmcControlServices(boost::asio::yield_context yield) 167 { 168 uint16_t serviceValue = 0; 169 try 170 { 171 auto sdbusp = getSdBus(); 172 boost::system::error_code ec; 173 auto objectMap = sdbusp->yield_method_call<ObjectValueTree>( 174 yield, ec, getServiceConfigMgrName().c_str(), serviceConfigBasePath, 175 objectManagerIntf, getMgdObjMethod); 176 checkAndThrowError(ec, "GetMangagedObjects for service cfg failed"); 177 178 for (const auto& services : bmcServices) 179 { 180 for (const auto& obj : objectMap) 181 { 182 if (boost::algorithm::starts_with(obj.first.str, 183 services.second)) 184 { 185 serviceValue |= getEnabledValue(obj.second) 186 << services.first; 187 break; 188 } 189 } 190 } 191 } 192 catch (sdbusplus::exception::SdBusError& e) 193 { 194 return ipmi::responseUnspecifiedError(); 195 } 196 // Bit 14 should match bit 15 as single service maintains Video & USB 197 // redirection 198 serviceValue |= (serviceValue & maskBit15) >> 1; 199 return ipmi::responseSuccess(serviceValue); 200 } 201 202 void register_netfn_bmc_control_functions() 203 { 204 ipmi::registerHandler(ipmi::prioOpenBmcBase, netfnIntcOEMGeneral, 205 static_cast<ipmi_cmd_t>( 206 IPMINetFnIntelOemGeneralCmds::controlBmcServices), 207 ipmi::Privilege::Admin, setBmcControlServices); 208 209 ipmi::registerHandler( 210 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral, 211 static_cast<ipmi_cmd_t>( 212 IPMINetFnIntelOemGeneralCmds::getBmcServiceStatus), 213 ipmi::Privilege::User, getBmcControlServices); 214 } 215 } // namespace ipmi 216