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