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