blob: 7265bbb64d167cd92e8c021cf31ded213fb97d1e [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>
kasunath511b06c2021-08-04 20:16:30 -070021#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
Jonathan Domanee03a9b2020-11-11 12:56:23 -080022
23#include <iostream>
24#include <type_traits>
25#include <utility>
26#include <variant>
27
28namespace cpu_info
29{
30
31using namespace sdbusplus::xyz::openbmc_project;
32using PowerState = State::server::Host::HostState;
kasunath511b06c2021-08-04 20:16:30 -070033using OsState = State::OperatingSystem::server::Status::OSStatus;
Jonathan Domanee03a9b2020-11-11 12:56:23 -080034
35HostState hostState = HostState::off;
36static PowerState powerState = PowerState::Off;
kasunath511b06c2021-08-04 20:16:30 -070037static OsState osState = OsState::Inactive;
Jonathan Domanee03a9b2020-11-11 12:56:23 -080038static bool biosDone = false;
Jonathan Doman49ea8302022-05-26 14:29:46 -070039static std::vector<HostStateHandler> hostStateCallbacks;
Jonathan Domanee03a9b2020-11-11 12:56:23 -080040
41static std::shared_ptr<sdbusplus::asio::connection> dbusConn;
42
Jonathan Doman49ea8302022-05-26 14:29:46 -070043void addHostStateCallback(HostStateHandler cb)
44{
45 hostStateCallbacks.push_back(cb);
46}
47
Jonathan Domanee03a9b2020-11-11 12:56:23 -080048static void updateHostState()
49{
Jonathan Doman49ea8302022-05-26 14:29:46 -070050 HostState prevState = hostState;
Jonathan Domanee03a9b2020-11-11 12:56:23 -080051 if (powerState == PowerState::Off)
52 {
53 hostState = HostState::off;
54 // Make sure that we don't inadvertently jump back to PostComplete if
55 // the HW status happens to turn back on before the biosDone goes false,
56 // since the two signals come from different services and there is no
57 // tight guarantee about their relationship.
58 biosDone = false;
kasunath511b06c2021-08-04 20:16:30 -070059 // Setting osState to inactive for the same reason as above.
60 osState = OsState::Inactive;
Jonathan Domanee03a9b2020-11-11 12:56:23 -080061 }
kasunath511b06c2021-08-04 20:16:30 -070062 // Both biosDone and OsState tell us about the POST done status. At least
63 // one of them should indicate that the POST is done.
64 // According to openbmc_project/State/OperatingSystem/Status.interface.yaml
65 // Only "Inactive" indicates that the POST is not done. All the other
66 // statuses (CBoot, PXEBoot, DiagBoot, CDROMBoot, ROMBoot, BootComplete,
67 // Standby) indicate that the POST is done.
68 else if ((!biosDone) && (osState == OsState::Inactive))
Jonathan Domanee03a9b2020-11-11 12:56:23 -080069 {
70 hostState = HostState::postInProgress;
71 }
72 else
73 {
74 hostState = HostState::postComplete;
75 }
76 DEBUG_PRINT << "new host state: " << static_cast<int>(hostState) << "\n";
Jonathan Doman49ea8302022-05-26 14:29:46 -070077
78 if (prevState != hostState)
79 {
80 for (const auto& cb : hostStateCallbacks)
81 {
82 cb(prevState, hostState);
83 }
84 }
Jonathan Domanee03a9b2020-11-11 12:56:23 -080085}
86
87void updatePowerState(const std::string& newState)
88{
89 powerState = State::server::Host::convertHostStateFromString(newState);
90 updateHostState();
91}
92
93void updateBiosDone(bool newState)
94{
95 biosDone = newState;
96 updateHostState();
97}
98
kasunath511b06c2021-08-04 20:16:30 -070099void updateOsState(const std::string& newState)
100{
101 // newState might not contain the full path. It might just contain the enum
102 // string (By the time I am writing this, its not returning the full path).
103 // Full string:
104 // "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby". Just
105 // the string for enum: "Standby". If the newState doesn't contain the full
106 // string, convertOSStatusFromString will fail. Prepend the full path if
107 // needed.
108 std::string full_path = newState;
109 if (newState.find("xyz.") == std::string::npos)
110 {
111 full_path =
112 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus." +
113 newState;
114 }
115
116 try
117 {
118 osState =
119 State::OperatingSystem::server::Status::convertOSStatusFromString(
120 full_path);
121 }
122 catch (const sdbusplus::exception::InvalidEnumString& ex)
123 {
124 std::cerr << "Invalid OperatingSystem Status: " << full_path << "\n";
125 osState = OsState::Inactive;
126 }
127 updateHostState();
128}
129
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800130/**
131 * Register a handler to be called whenever the given property is changed. Also
132 * call the handler once immediately (asynchronously) with the current property
133 * value.
134 *
135 * Since this necessarily reads all properties in the given interface, type
136 * information about the interface may need to be provided via
137 * CustomVariantArgs.
138 *
139 * @tparam CustomVariantTypes Any property types contained in the interface
140 * beyond the base data types (numeric and
141 * string-like types) and Handler's param type.
142 * @tparam Handler Automatically deduced. Must be a callable taking a
143 * single parameter whose type matches the property.
144 *
145 * @param[in] service D-Bus service name.
146 * @param[in] object D-Bus object name.
147 * @param[in] interface D-Bus interface name.
148 * @param[in] propertyName D-Bus property name.
149 * @param[in] handler Callable to be called immediately and upon any
150 * changes in the property value.
151 * @param[out] propertiesChangedMatch Optional pointer to receive a D-Bus
152 * match object, if you need to manage its
153 * lifetime.
154 * @param[out] interfacesAddedMatch Optional pointer to receive a D-Bus
155 * match object, if you need to manage its
156 * lifetime.
157 */
158template <typename... CustomVariantTypes, typename Handler>
159static void subscribeToProperty(
160 const char* service, const char* object, const char* interface,
161 const char* propertyName, Handler&& handler,
162 sdbusplus::bus::match_t** propertiesChangedMatch = nullptr,
163 sdbusplus::bus::match_t** interfacesAddedMatch = nullptr)
164{
165 // Type of first parameter to Handler, with const/& removed
166 using PropertyType = std::remove_const_t<std::remove_reference_t<
167 std::tuple_element_t<0, boost::callable_traits::args_t<Handler>>>>;
168 // Base data types which we can handle by default
Patrick Williamsa427dd12021-07-15 08:53:46 -0500169 using InterfaceVariant = typename sdbusplus::utility::dedup_variant_t<
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800170 PropertyType, CustomVariantTypes..., bool, uint8_t, uint16_t, int16_t,
171 uint32_t, int32_t, uint64_t, int64_t, size_t, ssize_t, double,
172 std::string, sdbusplus::message::object_path>;
173
174 sdbusplus::asio::getProperty<PropertyType>(
175 *dbusConn, service, object, interface, propertyName,
176 [handler, propertyName = std::string(propertyName)](
177 boost::system::error_code ec, const PropertyType& newValue) {
178 if (ec)
179 {
180 std::cerr << "Failed to read property " << propertyName << ": "
181 << ec << "\n";
182 return;
183 }
184 handler(newValue);
185 });
186
187 using ChangedPropertiesType =
188 std::vector<std::pair<std::string, InterfaceVariant>>;
189
190 // Define some logic which is common to the two match callbacks, since they
191 // both have to loop through all the properties in the interface.
192 auto commonPropHandler = [propertyName = std::string(propertyName),
193 handler = std::forward<Handler>(handler)](
194 const ChangedPropertiesType& changedProps) {
195 for (const auto& [changedProp, newValue] : changedProps)
196 {
197 if (changedProp == propertyName)
198 {
199 const auto* actualVal = std::get_if<PropertyType>(&newValue);
200 if (actualVal != nullptr)
201 {
202 DEBUG_PRINT << "New value for " << propertyName << ": "
203 << *actualVal << "\n";
204 handler(*actualVal);
205 }
206 else
207 {
208 std::cerr << "Property " << propertyName
209 << " had unexpected type\n";
210 }
211 break;
212 }
213 }
214 };
215
216 // Set up a match for PropertiesChanged signal
217 auto* propMatch = new sdbusplus::bus::match_t(
218 *dbusConn,
219 sdbusplus::bus::match::rules::sender(service) +
220 sdbusplus::bus::match::rules::propertiesChanged(object, interface),
221 [commonPropHandler](sdbusplus::message::message& reply) {
222 ChangedPropertiesType changedProps;
223 // ignore first param (interface name), it has to be correct
224 reply.read(std::string(), changedProps);
225
226 DEBUG_PRINT << "PropertiesChanged handled\n";
227 commonPropHandler(changedProps);
228 });
229
230 // Set up a match for the InterfacesAdded signal from the service's
231 // ObjectManager. This is useful in the case where the object is not added
232 // yet, and when it's added they choose to not emit PropertiesChanged. So in
233 // order to see the initial value when it comes, we need to watch this too.
234 auto* intfMatch = new sdbusplus::bus::match_t(
235 *dbusConn,
236 sdbusplus::bus::match::rules::sender(service) +
237 sdbusplus::bus::match::rules::interfacesAdded(),
238 [object = std::string(object), interface = std::string(interface),
239 commonPropHandler](sdbusplus::message::message& reply) {
240 sdbusplus::message::object_path changedObject;
241 reply.read(changedObject);
242 if (changedObject != object)
243 {
244 return;
245 }
246
247 std::vector<std::pair<std::string, ChangedPropertiesType>>
248 changedInterfaces;
249 reply.read(changedInterfaces);
250
251 for (const auto& [changedInterface, changedProps] :
252 changedInterfaces)
253 {
254 if (changedInterface != interface)
255 {
256 continue;
257 }
258
259 DEBUG_PRINT << "InterfacesAdded handled\n";
260 commonPropHandler(changedProps);
261 }
262 });
263
264 if (propertiesChangedMatch != nullptr)
265 {
266 *propertiesChangedMatch = propMatch;
267 }
268
269 if (interfacesAddedMatch != nullptr)
270 {
271 *interfacesAddedMatch = intfMatch;
272 }
273}
274
275void hostStateSetup(const std::shared_ptr<sdbusplus::asio::connection>& conn)
276{
277 static bool initialized = false;
278 if (initialized)
279 {
280 return;
281 }
282
283 dbusConn = conn;
284
285 // Leak the returned match objects. We want them to run forever.
286 subscribeToProperty(
287 "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
288 State::server::Host::interface, "CurrentHostState", updatePowerState);
289 subscribeToProperty("xyz.openbmc_project.Host.Misc.Manager",
290 "/xyz/openbmc_project/misc/platform_state",
291 "xyz.openbmc_project.State.Host.Misc", "CoreBiosDone",
292 updateBiosDone);
kasunath511b06c2021-08-04 20:16:30 -0700293 // xyz.openbmc_project.Host.Misc.Manager has Intel specific dependencies.
294 // If it is not available, then we can use the OperatingSystemState in
295 // xyz.openbmc_project.State.OperatingSystem. According to x86-power-control
296 // repo, OperatingSystemState should return "standby" once the POST is
297 // asserted.
298 subscribeToProperty("xyz.openbmc_project.State.OperatingSystem",
299 "/xyz/openbmc_project/state/os",
300 "xyz.openbmc_project.State.OperatingSystem.Status",
301 "OperatingSystemState", updateOsState);
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800302
303 initialized = true;
304}
305
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700306namespace dbus
307{
308boost::asio::io_context& getIOContext()
309{
310 static boost::asio::io_context ioc;
311 return ioc;
312}
313std::shared_ptr<sdbusplus::asio::connection> getConnection()
314{
315 static auto conn =
316 std::make_shared<sdbusplus::asio::connection>(getIOContext());
317 return conn;
318}
319} // namespace dbus
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800320} // namespace cpu_info