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