blob: 96e8e6f6f55838bea4639e4ba0a01abb08f3f2d3 [file] [log] [blame]
Jonathan Domanee03a9b2020-11-11 12:56:23 -08001// Copyright (c) 2021 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "cpuinfo_utils.hpp"
16
17// Include the server headers to get the enum<->string conversion functions
18#include <boost/algorithm/string/predicate.hpp>
19#include <sdbusplus/asio/property.hpp>
20#include <xyz/openbmc_project/State/Host/server.hpp>
21
22#include <iostream>
23#include <type_traits>
24#include <utility>
25#include <variant>
26
27namespace cpu_info
28{
29
30using namespace sdbusplus::xyz::openbmc_project;
31using PowerState = State::server::Host::HostState;
32
33HostState hostState = HostState::off;
34static PowerState powerState = PowerState::Off;
35static bool biosDone = false;
36
37static std::shared_ptr<sdbusplus::asio::connection> dbusConn;
38
39static void updateHostState()
40{
41 if (powerState == PowerState::Off)
42 {
43 hostState = HostState::off;
44 // Make sure that we don't inadvertently jump back to PostComplete if
45 // the HW status happens to turn back on before the biosDone goes false,
46 // since the two signals come from different services and there is no
47 // tight guarantee about their relationship.
48 biosDone = false;
49 }
50 else if (!biosDone)
51 {
52 hostState = HostState::postInProgress;
53 }
54 else
55 {
56 hostState = HostState::postComplete;
57 }
58 DEBUG_PRINT << "new host state: " << static_cast<int>(hostState) << "\n";
59}
60
61void updatePowerState(const std::string& newState)
62{
63 powerState = State::server::Host::convertHostStateFromString(newState);
64 updateHostState();
65}
66
67void updateBiosDone(bool newState)
68{
69 biosDone = newState;
70 updateHostState();
71}
72
73/**
74 * Register a handler to be called whenever the given property is changed. Also
75 * call the handler once immediately (asynchronously) with the current property
76 * value.
77 *
78 * Since this necessarily reads all properties in the given interface, type
79 * information about the interface may need to be provided via
80 * CustomVariantArgs.
81 *
82 * @tparam CustomVariantTypes Any property types contained in the interface
83 * beyond the base data types (numeric and
84 * string-like types) and Handler's param type.
85 * @tparam Handler Automatically deduced. Must be a callable taking a
86 * single parameter whose type matches the property.
87 *
88 * @param[in] service D-Bus service name.
89 * @param[in] object D-Bus object name.
90 * @param[in] interface D-Bus interface name.
91 * @param[in] propertyName D-Bus property name.
92 * @param[in] handler Callable to be called immediately and upon any
93 * changes in the property value.
94 * @param[out] propertiesChangedMatch Optional pointer to receive a D-Bus
95 * match object, if you need to manage its
96 * lifetime.
97 * @param[out] interfacesAddedMatch Optional pointer to receive a D-Bus
98 * match object, if you need to manage its
99 * lifetime.
100 */
101template <typename... CustomVariantTypes, typename Handler>
102static void subscribeToProperty(
103 const char* service, const char* object, const char* interface,
104 const char* propertyName, Handler&& handler,
105 sdbusplus::bus::match_t** propertiesChangedMatch = nullptr,
106 sdbusplus::bus::match_t** interfacesAddedMatch = nullptr)
107{
108 // Type of first parameter to Handler, with const/& removed
109 using PropertyType = std::remove_const_t<std::remove_reference_t<
110 std::tuple_element_t<0, boost::callable_traits::args_t<Handler>>>>;
111 // Base data types which we can handle by default
Patrick Williamsa427dd12021-07-15 08:53:46 -0500112 using InterfaceVariant = typename sdbusplus::utility::dedup_variant_t<
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800113 PropertyType, CustomVariantTypes..., bool, uint8_t, uint16_t, int16_t,
114 uint32_t, int32_t, uint64_t, int64_t, size_t, ssize_t, double,
115 std::string, sdbusplus::message::object_path>;
116
117 sdbusplus::asio::getProperty<PropertyType>(
118 *dbusConn, service, object, interface, propertyName,
119 [handler, propertyName = std::string(propertyName)](
120 boost::system::error_code ec, const PropertyType& newValue) {
121 if (ec)
122 {
123 std::cerr << "Failed to read property " << propertyName << ": "
124 << ec << "\n";
125 return;
126 }
127 handler(newValue);
128 });
129
130 using ChangedPropertiesType =
131 std::vector<std::pair<std::string, InterfaceVariant>>;
132
133 // Define some logic which is common to the two match callbacks, since they
134 // both have to loop through all the properties in the interface.
135 auto commonPropHandler = [propertyName = std::string(propertyName),
136 handler = std::forward<Handler>(handler)](
137 const ChangedPropertiesType& changedProps) {
138 for (const auto& [changedProp, newValue] : changedProps)
139 {
140 if (changedProp == propertyName)
141 {
142 const auto* actualVal = std::get_if<PropertyType>(&newValue);
143 if (actualVal != nullptr)
144 {
145 DEBUG_PRINT << "New value for " << propertyName << ": "
146 << *actualVal << "\n";
147 handler(*actualVal);
148 }
149 else
150 {
151 std::cerr << "Property " << propertyName
152 << " had unexpected type\n";
153 }
154 break;
155 }
156 }
157 };
158
159 // Set up a match for PropertiesChanged signal
160 auto* propMatch = new sdbusplus::bus::match_t(
161 *dbusConn,
162 sdbusplus::bus::match::rules::sender(service) +
163 sdbusplus::bus::match::rules::propertiesChanged(object, interface),
164 [commonPropHandler](sdbusplus::message::message& reply) {
165 ChangedPropertiesType changedProps;
166 // ignore first param (interface name), it has to be correct
167 reply.read(std::string(), changedProps);
168
169 DEBUG_PRINT << "PropertiesChanged handled\n";
170 commonPropHandler(changedProps);
171 });
172
173 // Set up a match for the InterfacesAdded signal from the service's
174 // ObjectManager. This is useful in the case where the object is not added
175 // yet, and when it's added they choose to not emit PropertiesChanged. So in
176 // order to see the initial value when it comes, we need to watch this too.
177 auto* intfMatch = new sdbusplus::bus::match_t(
178 *dbusConn,
179 sdbusplus::bus::match::rules::sender(service) +
180 sdbusplus::bus::match::rules::interfacesAdded(),
181 [object = std::string(object), interface = std::string(interface),
182 commonPropHandler](sdbusplus::message::message& reply) {
183 sdbusplus::message::object_path changedObject;
184 reply.read(changedObject);
185 if (changedObject != object)
186 {
187 return;
188 }
189
190 std::vector<std::pair<std::string, ChangedPropertiesType>>
191 changedInterfaces;
192 reply.read(changedInterfaces);
193
194 for (const auto& [changedInterface, changedProps] :
195 changedInterfaces)
196 {
197 if (changedInterface != interface)
198 {
199 continue;
200 }
201
202 DEBUG_PRINT << "InterfacesAdded handled\n";
203 commonPropHandler(changedProps);
204 }
205 });
206
207 if (propertiesChangedMatch != nullptr)
208 {
209 *propertiesChangedMatch = propMatch;
210 }
211
212 if (interfacesAddedMatch != nullptr)
213 {
214 *interfacesAddedMatch = intfMatch;
215 }
216}
217
218void hostStateSetup(const std::shared_ptr<sdbusplus::asio::connection>& conn)
219{
220 static bool initialized = false;
221 if (initialized)
222 {
223 return;
224 }
225
226 dbusConn = conn;
227
228 // Leak the returned match objects. We want them to run forever.
229 subscribeToProperty(
230 "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
231 State::server::Host::interface, "CurrentHostState", updatePowerState);
232 subscribeToProperty("xyz.openbmc_project.Host.Misc.Manager",
233 "/xyz/openbmc_project/misc/platform_state",
234 "xyz.openbmc_project.State.Host.Misc", "CoreBiosDone",
235 updateBiosDone);
236
237 initialized = true;
238}
239
240} // namespace cpu_info