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