blob: 5c465859aef004f5217b93246492115dc80e3d40 [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
Jason M. Bills33ae81f2023-04-26 09:06:08 -070031using namespace sdbusplus::server::xyz::openbmc_project;
32using PowerState = state::Host::HostState;
33using OsState = state::operating_system::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{
Jason M. Bills33ae81f2023-04-26 09:06:08 -070089 powerState = state::Host::convertHostStateFromString(newState);
Jonathan Domanee03a9b2020-11-11 12:56:23 -080090 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 {
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700118 osState = state::operating_system::Status::convertOSStatusFromString(
119 full_path);
kasunath511b06c2021-08-04 20:16:30 -0700120 }
121 catch (const sdbusplus::exception::InvalidEnumString& ex)
122 {
123 std::cerr << "Invalid OperatingSystem Status: " << full_path << "\n";
124 osState = OsState::Inactive;
125 }
126 updateHostState();
127}
128
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800129/**
130 * Register a handler to be called whenever the given property is changed. Also
131 * call the handler once immediately (asynchronously) with the current property
132 * value.
133 *
134 * Since this necessarily reads all properties in the given interface, type
135 * information about the interface may need to be provided via
136 * CustomVariantArgs.
137 *
138 * @tparam CustomVariantTypes Any property types contained in the interface
139 * beyond the base data types (numeric and
140 * string-like types) and Handler's param type.
141 * @tparam Handler Automatically deduced. Must be a callable taking a
142 * single parameter whose type matches the property.
143 *
144 * @param[in] service D-Bus service name.
145 * @param[in] object D-Bus object name.
146 * @param[in] interface D-Bus interface name.
147 * @param[in] propertyName D-Bus property name.
148 * @param[in] handler Callable to be called immediately and upon any
149 * changes in the property value.
150 * @param[out] propertiesChangedMatch Optional pointer to receive a D-Bus
151 * match object, if you need to manage its
152 * lifetime.
153 * @param[out] interfacesAddedMatch Optional pointer to receive a D-Bus
154 * match object, if you need to manage its
155 * lifetime.
156 */
157template <typename... CustomVariantTypes, typename Handler>
158static void subscribeToProperty(
159 const char* service, const char* object, const char* interface,
160 const char* propertyName, Handler&& handler,
161 sdbusplus::bus::match_t** propertiesChangedMatch = nullptr,
162 sdbusplus::bus::match_t** interfacesAddedMatch = nullptr)
163{
164 // Type of first parameter to Handler, with const/& removed
165 using PropertyType = std::remove_const_t<std::remove_reference_t<
166 std::tuple_element_t<0, boost::callable_traits::args_t<Handler>>>>;
167 // Base data types which we can handle by default
Patrick Williamsa427dd12021-07-15 08:53:46 -0500168 using InterfaceVariant = typename sdbusplus::utility::dedup_variant_t<
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800169 PropertyType, CustomVariantTypes..., bool, uint8_t, uint16_t, int16_t,
170 uint32_t, int32_t, uint64_t, int64_t, size_t, ssize_t, double,
171 std::string, sdbusplus::message::object_path>;
172
173 sdbusplus::asio::getProperty<PropertyType>(
174 *dbusConn, service, object, interface, propertyName,
175 [handler, propertyName = std::string(propertyName)](
176 boost::system::error_code ec, const PropertyType& newValue) {
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500177 if (ec)
178 {
179 std::cerr << "Failed to read property " << propertyName << ": "
180 << ec << "\n";
181 return;
182 }
183 handler(newValue);
Patrick Williamsbadedf12023-10-20 11:19:42 -0500184 });
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800185
186 using ChangedPropertiesType =
187 std::vector<std::pair<std::string, InterfaceVariant>>;
188
189 // Define some logic which is common to the two match callbacks, since they
190 // both have to loop through all the properties in the interface.
191 auto commonPropHandler = [propertyName = std::string(propertyName),
192 handler = std::forward<Handler>(handler)](
193 const ChangedPropertiesType& changedProps) {
194 for (const auto& [changedProp, newValue] : changedProps)
195 {
196 if (changedProp == propertyName)
197 {
198 const auto* actualVal = std::get_if<PropertyType>(&newValue);
199 if (actualVal != nullptr)
200 {
201 DEBUG_PRINT << "New value for " << propertyName << ": "
202 << *actualVal << "\n";
203 handler(*actualVal);
204 }
205 else
206 {
207 std::cerr << "Property " << propertyName
208 << " had unexpected type\n";
209 }
210 break;
211 }
212 }
213 };
214
215 // Set up a match for PropertiesChanged signal
216 auto* propMatch = new sdbusplus::bus::match_t(
217 *dbusConn,
218 sdbusplus::bus::match::rules::sender(service) +
219 sdbusplus::bus::match::rules::propertiesChanged(object, interface),
Patrick Williams77b9c472022-07-22 19:26:57 -0500220 [commonPropHandler](sdbusplus::message_t& reply) {
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500221 ChangedPropertiesType changedProps;
222 // ignore first param (interface name), it has to be correct
223 reply.read(std::string(), changedProps);
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800224
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500225 DEBUG_PRINT << "PropertiesChanged handled\n";
226 commonPropHandler(changedProps);
Patrick Williamsbadedf12023-10-20 11:19:42 -0500227 });
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800228
229 // Set up a match for the InterfacesAdded signal from the service's
230 // ObjectManager. This is useful in the case where the object is not added
231 // yet, and when it's added they choose to not emit PropertiesChanged. So in
232 // order to see the initial value when it comes, we need to watch this too.
233 auto* intfMatch = new sdbusplus::bus::match_t(
234 *dbusConn,
235 sdbusplus::bus::match::rules::sender(service) +
236 sdbusplus::bus::match::rules::interfacesAdded(),
237 [object = std::string(object), interface = std::string(interface),
Patrick Williams77b9c472022-07-22 19:26:57 -0500238 commonPropHandler](sdbusplus::message_t& reply) {
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500239 sdbusplus::message::object_path changedObject;
240 reply.read(changedObject);
241 if (changedObject != object)
242 {
243 return;
244 }
245
246 std::vector<std::pair<std::string, ChangedPropertiesType>>
247 changedInterfaces;
248 reply.read(changedInterfaces);
249
250 for (const auto& [changedInterface, changedProps] : changedInterfaces)
251 {
252 if (changedInterface != interface)
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800253 {
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500254 continue;
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800255 }
256
Patrick Williamsc39d3df2023-05-10 07:51:14 -0500257 DEBUG_PRINT << "InterfacesAdded handled\n";
258 commonPropHandler(changedProps);
259 }
Patrick Williamsbadedf12023-10-20 11:19:42 -0500260 });
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800261
262 if (propertiesChangedMatch != nullptr)
263 {
264 *propertiesChangedMatch = propMatch;
265 }
266
267 if (interfacesAddedMatch != nullptr)
268 {
269 *interfacesAddedMatch = intfMatch;
270 }
271}
272
273void hostStateSetup(const std::shared_ptr<sdbusplus::asio::connection>& conn)
274{
275 static bool initialized = false;
276 if (initialized)
277 {
278 return;
279 }
280
281 dbusConn = conn;
282
283 // Leak the returned match objects. We want them to run forever.
284 subscribeToProperty(
285 "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700286 state::Host::interface, "CurrentHostState", updatePowerState);
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800287 subscribeToProperty("xyz.openbmc_project.Host.Misc.Manager",
288 "/xyz/openbmc_project/misc/platform_state",
289 "xyz.openbmc_project.State.Host.Misc", "CoreBiosDone",
290 updateBiosDone);
kasunath511b06c2021-08-04 20:16:30 -0700291 // xyz.openbmc_project.Host.Misc.Manager has Intel specific dependencies.
292 // If it is not available, then we can use the OperatingSystemState in
293 // xyz.openbmc_project.State.OperatingSystem. According to x86-power-control
294 // repo, OperatingSystemState should return "standby" once the POST is
295 // asserted.
296 subscribeToProperty("xyz.openbmc_project.State.OperatingSystem",
297 "/xyz/openbmc_project/state/os",
298 "xyz.openbmc_project.State.OperatingSystem.Status",
299 "OperatingSystemState", updateOsState);
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800300
301 initialized = true;
302}
303
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700304namespace dbus
305{
306boost::asio::io_context& getIOContext()
307{
308 static boost::asio::io_context ioc;
309 return ioc;
310}
311std::shared_ptr<sdbusplus::asio::connection> getConnection()
312{
313 static auto conn =
314 std::make_shared<sdbusplus::asio::connection>(getIOContext());
315 return conn;
316}
317} // namespace dbus
Jonathan Domanee03a9b2020-11-11 12:56:23 -0800318} // namespace cpu_info