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