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 <types.hpp>
24
25 #include <variant>
26
27 namespace ipmi
28 {
29 void register_netfn_bmc_control_functions() __attribute__((constructor));
30
31 static constexpr uint8_t rmcpServiceBitPos = 3;
32 static constexpr uint8_t webServiceBitPos = 5;
33 static constexpr uint8_t solServiceBitPos = 6;
34 static constexpr uint8_t kvmServiceBitPos = 15;
35
36 static constexpr std::array<std::pair<uint8_t, const char*>, 4> bmcServices = {{
37 // {bit position for service, service name}
38 {rmcpServiceBitPos, "phosphor-ipmi-net"},
39 {webServiceBitPos, "bmcweb"},
40 {solServiceBitPos, "obmc-console-ssh"},
41 {kvmServiceBitPos, "obmc-ikvm"},
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
getServiceConfigMgrName()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_t& 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
checkAndThrowError(boost::system::error_code & ec,const std::string & msg)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
getEnabledValue(const DbusInterfaceMap & intfMap)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
setBmcControlServices(boost::asio::yield_context yield,uint8_t state,uint16_t serviceValue)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.filename(),
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 ipmi::DbusVariant(!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_t& e)
161 {
162 return ipmi::responseUnspecifiedError();
163 }
164 return ipmi::responseSuccess();
165 }
166
getBmcControlServices(boost::asio::yield_context yield)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.filename(),
184 services.second))
185 {
186 serviceValue |= getEnabledValue(obj.second)
187 << services.first;
188 break;
189 }
190 }
191 }
192 }
193 catch (const sdbusplus::exception_t& 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
register_netfn_bmc_control_functions()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