1ee03a9b5SJonathan Doman // Copyright (c) 2021 Intel Corporation
2ee03a9b5SJonathan Doman //
3ee03a9b5SJonathan Doman // Licensed under the Apache License, Version 2.0 (the "License");
4ee03a9b5SJonathan Doman // you may not use this file except in compliance with the License.
5ee03a9b5SJonathan Doman // You may obtain a copy of the License at
6ee03a9b5SJonathan Doman //
7ee03a9b5SJonathan Doman // http://www.apache.org/licenses/LICENSE-2.0
8ee03a9b5SJonathan Doman //
9ee03a9b5SJonathan Doman // Unless required by applicable law or agreed to in writing, software
10ee03a9b5SJonathan Doman // distributed under the License is distributed on an "AS IS" BASIS,
11ee03a9b5SJonathan Doman // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12ee03a9b5SJonathan Doman // See the License for the specific language governing permissions and
13ee03a9b5SJonathan Doman // limitations under the License.
14ee03a9b5SJonathan Doman
15ee03a9b5SJonathan Doman #include "cpuinfo_utils.hpp"
16ee03a9b5SJonathan Doman
17ee03a9b5SJonathan Doman // Include the server headers to get the enum<->string conversion functions
18ee03a9b5SJonathan Doman #include <boost/algorithm/string/predicate.hpp>
19ee03a9b5SJonathan Doman #include <sdbusplus/asio/property.hpp>
20ee03a9b5SJonathan Doman #include <xyz/openbmc_project/State/Host/server.hpp>
21511b06c0Skasunath #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
22ee03a9b5SJonathan Doman
23ee03a9b5SJonathan Doman #include <iostream>
24ee03a9b5SJonathan Doman #include <type_traits>
25ee03a9b5SJonathan Doman #include <utility>
26ee03a9b5SJonathan Doman #include <variant>
27ee03a9b5SJonathan Doman
28ee03a9b5SJonathan Doman namespace cpu_info
29ee03a9b5SJonathan Doman {
30ee03a9b5SJonathan Doman
3133ae81feSJason M. Bills using namespace sdbusplus::server::xyz::openbmc_project;
3233ae81feSJason M. Bills using PowerState = state::Host::HostState;
3333ae81feSJason M. Bills using OsState = state::operating_system::Status::OSStatus;
34ee03a9b5SJonathan Doman
35ee03a9b5SJonathan Doman HostState hostState = HostState::off;
36ee03a9b5SJonathan Doman static PowerState powerState = PowerState::Off;
37511b06c0Skasunath static OsState osState = OsState::Inactive;
38ee03a9b5SJonathan Doman static bool biosDone = false;
3949ea830eSJonathan Doman static std::vector<HostStateHandler> hostStateCallbacks;
40ee03a9b5SJonathan Doman
41ee03a9b5SJonathan Doman static std::shared_ptr<sdbusplus::asio::connection> dbusConn;
42ee03a9b5SJonathan Doman
addHostStateCallback(HostStateHandler cb)4349ea830eSJonathan Doman void addHostStateCallback(HostStateHandler cb)
4449ea830eSJonathan Doman {
4549ea830eSJonathan Doman hostStateCallbacks.push_back(cb);
4649ea830eSJonathan Doman }
4749ea830eSJonathan Doman
updateHostState()48ee03a9b5SJonathan Doman static void updateHostState()
49ee03a9b5SJonathan Doman {
5049ea830eSJonathan Doman HostState prevState = hostState;
51ee03a9b5SJonathan Doman if (powerState == PowerState::Off)
52ee03a9b5SJonathan Doman {
53ee03a9b5SJonathan Doman hostState = HostState::off;
54ee03a9b5SJonathan Doman // Make sure that we don't inadvertently jump back to PostComplete if
55ee03a9b5SJonathan Doman // the HW status happens to turn back on before the biosDone goes false,
56ee03a9b5SJonathan Doman // since the two signals come from different services and there is no
57ee03a9b5SJonathan Doman // tight guarantee about their relationship.
58ee03a9b5SJonathan Doman biosDone = false;
59511b06c0Skasunath // Setting osState to inactive for the same reason as above.
60511b06c0Skasunath osState = OsState::Inactive;
61ee03a9b5SJonathan Doman }
62511b06c0Skasunath // Both biosDone and OsState tell us about the POST done status. At least
63511b06c0Skasunath // one of them should indicate that the POST is done.
64511b06c0Skasunath // According to openbmc_project/State/OperatingSystem/Status.interface.yaml
65511b06c0Skasunath // Only "Inactive" indicates that the POST is not done. All the other
66511b06c0Skasunath // statuses (CBoot, PXEBoot, DiagBoot, CDROMBoot, ROMBoot, BootComplete,
67511b06c0Skasunath // Standby) indicate that the POST is done.
68511b06c0Skasunath else if ((!biosDone) && (osState == OsState::Inactive))
69ee03a9b5SJonathan Doman {
70ee03a9b5SJonathan Doman hostState = HostState::postInProgress;
71ee03a9b5SJonathan Doman }
72ee03a9b5SJonathan Doman else
73ee03a9b5SJonathan Doman {
74ee03a9b5SJonathan Doman hostState = HostState::postComplete;
75ee03a9b5SJonathan Doman }
76ee03a9b5SJonathan Doman DEBUG_PRINT << "new host state: " << static_cast<int>(hostState) << "\n";
7749ea830eSJonathan Doman
7849ea830eSJonathan Doman if (prevState != hostState)
7949ea830eSJonathan Doman {
8049ea830eSJonathan Doman for (const auto& cb : hostStateCallbacks)
8149ea830eSJonathan Doman {
8249ea830eSJonathan Doman cb(prevState, hostState);
8349ea830eSJonathan Doman }
8449ea830eSJonathan Doman }
85ee03a9b5SJonathan Doman }
86ee03a9b5SJonathan Doman
updatePowerState(const std::string & newState)87ee03a9b5SJonathan Doman void updatePowerState(const std::string& newState)
88ee03a9b5SJonathan Doman {
8933ae81feSJason M. Bills powerState = state::Host::convertHostStateFromString(newState);
90ee03a9b5SJonathan Doman updateHostState();
91ee03a9b5SJonathan Doman }
92ee03a9b5SJonathan Doman
updateBiosDone(bool newState)93ee03a9b5SJonathan Doman void updateBiosDone(bool newState)
94ee03a9b5SJonathan Doman {
95ee03a9b5SJonathan Doman biosDone = newState;
96ee03a9b5SJonathan Doman updateHostState();
97ee03a9b5SJonathan Doman }
98ee03a9b5SJonathan Doman
updateOsState(const std::string & newState)99511b06c0Skasunath void updateOsState(const std::string& newState)
100511b06c0Skasunath {
101511b06c0Skasunath // newState might not contain the full path. It might just contain the enum
102511b06c0Skasunath // string (By the time I am writing this, its not returning the full path).
103511b06c0Skasunath // Full string:
104511b06c0Skasunath // "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby". Just
105511b06c0Skasunath // the string for enum: "Standby". If the newState doesn't contain the full
106511b06c0Skasunath // string, convertOSStatusFromString will fail. Prepend the full path if
107511b06c0Skasunath // needed.
108511b06c0Skasunath std::string full_path = newState;
109511b06c0Skasunath if (newState.find("xyz.") == std::string::npos)
110511b06c0Skasunath {
111511b06c0Skasunath full_path =
112511b06c0Skasunath "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus." +
113511b06c0Skasunath newState;
114511b06c0Skasunath }
115511b06c0Skasunath
116511b06c0Skasunath try
117511b06c0Skasunath {
11833ae81feSJason M. Bills osState = state::operating_system::Status::convertOSStatusFromString(
119511b06c0Skasunath full_path);
120511b06c0Skasunath }
121511b06c0Skasunath catch (const sdbusplus::exception::InvalidEnumString& ex)
122511b06c0Skasunath {
123511b06c0Skasunath std::cerr << "Invalid OperatingSystem Status: " << full_path << "\n";
124511b06c0Skasunath osState = OsState::Inactive;
125511b06c0Skasunath }
126511b06c0Skasunath updateHostState();
127511b06c0Skasunath }
128511b06c0Skasunath
129ee03a9b5SJonathan Doman /**
130ee03a9b5SJonathan Doman * Register a handler to be called whenever the given property is changed. Also
131ee03a9b5SJonathan Doman * call the handler once immediately (asynchronously) with the current property
132ee03a9b5SJonathan Doman * value.
133ee03a9b5SJonathan Doman *
134ee03a9b5SJonathan Doman * Since this necessarily reads all properties in the given interface, type
135ee03a9b5SJonathan Doman * information about the interface may need to be provided via
136ee03a9b5SJonathan Doman * CustomVariantArgs.
137ee03a9b5SJonathan Doman *
138ee03a9b5SJonathan Doman * @tparam CustomVariantTypes Any property types contained in the interface
139ee03a9b5SJonathan Doman * beyond the base data types (numeric and
140ee03a9b5SJonathan Doman * string-like types) and Handler's param type.
141ee03a9b5SJonathan Doman * @tparam Handler Automatically deduced. Must be a callable taking a
142ee03a9b5SJonathan Doman * single parameter whose type matches the property.
143ee03a9b5SJonathan Doman *
144ee03a9b5SJonathan Doman * @param[in] service D-Bus service name.
145ee03a9b5SJonathan Doman * @param[in] object D-Bus object name.
146ee03a9b5SJonathan Doman * @param[in] interface D-Bus interface name.
147ee03a9b5SJonathan Doman * @param[in] propertyName D-Bus property name.
148ee03a9b5SJonathan Doman * @param[in] handler Callable to be called immediately and upon any
149ee03a9b5SJonathan Doman * changes in the property value.
150ee03a9b5SJonathan Doman * @param[out] propertiesChangedMatch Optional pointer to receive a D-Bus
151ee03a9b5SJonathan Doman * match object, if you need to manage its
152ee03a9b5SJonathan Doman * lifetime.
153ee03a9b5SJonathan Doman * @param[out] interfacesAddedMatch Optional pointer to receive a D-Bus
154ee03a9b5SJonathan Doman * match object, if you need to manage its
155ee03a9b5SJonathan Doman * lifetime.
156ee03a9b5SJonathan Doman */
157ee03a9b5SJonathan Doman template <typename... CustomVariantTypes, typename Handler>
subscribeToProperty(const char * service,const char * object,const char * interface,const char * propertyName,Handler && handler,sdbusplus::bus::match_t ** propertiesChangedMatch=nullptr,sdbusplus::bus::match_t ** interfacesAddedMatch=nullptr)158ee03a9b5SJonathan Doman static void subscribeToProperty(
159ee03a9b5SJonathan Doman const char* service, const char* object, const char* interface,
160ee03a9b5SJonathan Doman const char* propertyName, Handler&& handler,
161ee03a9b5SJonathan Doman sdbusplus::bus::match_t** propertiesChangedMatch = nullptr,
162ee03a9b5SJonathan Doman sdbusplus::bus::match_t** interfacesAddedMatch = nullptr)
163ee03a9b5SJonathan Doman {
164ee03a9b5SJonathan Doman // Type of first parameter to Handler, with const/& removed
165ee03a9b5SJonathan Doman using PropertyType = std::remove_const_t<std::remove_reference_t<
166ee03a9b5SJonathan Doman std::tuple_element_t<0, boost::callable_traits::args_t<Handler>>>>;
167ee03a9b5SJonathan Doman // Base data types which we can handle by default
168a427dd1dSPatrick Williams using InterfaceVariant = typename sdbusplus::utility::dedup_variant_t<
169ee03a9b5SJonathan Doman PropertyType, CustomVariantTypes..., bool, uint8_t, uint16_t, int16_t,
170ee03a9b5SJonathan Doman uint32_t, int32_t, uint64_t, int64_t, size_t, ssize_t, double,
171ee03a9b5SJonathan Doman std::string, sdbusplus::message::object_path>;
172ee03a9b5SJonathan Doman
173ee03a9b5SJonathan Doman sdbusplus::asio::getProperty<PropertyType>(
174ee03a9b5SJonathan Doman *dbusConn, service, object, interface, propertyName,
175ee03a9b5SJonathan Doman [handler, propertyName = std::string(propertyName)](
176ee03a9b5SJonathan Doman boost::system::error_code ec, const PropertyType& newValue) {
177ee03a9b5SJonathan Doman if (ec)
178ee03a9b5SJonathan Doman {
179ee03a9b5SJonathan Doman std::cerr << "Failed to read property " << propertyName << ": "
180ee03a9b5SJonathan Doman << ec << "\n";
181ee03a9b5SJonathan Doman return;
182ee03a9b5SJonathan Doman }
183ee03a9b5SJonathan Doman handler(newValue);
184ee03a9b5SJonathan Doman });
185ee03a9b5SJonathan Doman
186ee03a9b5SJonathan Doman using ChangedPropertiesType =
187ee03a9b5SJonathan Doman std::vector<std::pair<std::string, InterfaceVariant>>;
188ee03a9b5SJonathan Doman
189ee03a9b5SJonathan Doman // Define some logic which is common to the two match callbacks, since they
190ee03a9b5SJonathan Doman // both have to loop through all the properties in the interface.
191ee03a9b5SJonathan Doman auto commonPropHandler = [propertyName = std::string(propertyName),
192ee03a9b5SJonathan Doman handler = std::forward<Handler>(handler)](
193ee03a9b5SJonathan Doman const ChangedPropertiesType& changedProps) {
194ee03a9b5SJonathan Doman for (const auto& [changedProp, newValue] : changedProps)
195ee03a9b5SJonathan Doman {
196ee03a9b5SJonathan Doman if (changedProp == propertyName)
197ee03a9b5SJonathan Doman {
198ee03a9b5SJonathan Doman const auto* actualVal = std::get_if<PropertyType>(&newValue);
199ee03a9b5SJonathan Doman if (actualVal != nullptr)
200ee03a9b5SJonathan Doman {
201ee03a9b5SJonathan Doman DEBUG_PRINT << "New value for " << propertyName << ": "
202ee03a9b5SJonathan Doman << *actualVal << "\n";
203ee03a9b5SJonathan Doman handler(*actualVal);
204ee03a9b5SJonathan Doman }
205ee03a9b5SJonathan Doman else
206ee03a9b5SJonathan Doman {
207ee03a9b5SJonathan Doman std::cerr << "Property " << propertyName
208ee03a9b5SJonathan Doman << " had unexpected type\n";
209ee03a9b5SJonathan Doman }
210ee03a9b5SJonathan Doman break;
211ee03a9b5SJonathan Doman }
212ee03a9b5SJonathan Doman }
213ee03a9b5SJonathan Doman };
214ee03a9b5SJonathan Doman
215ee03a9b5SJonathan Doman // Set up a match for PropertiesChanged signal
216ee03a9b5SJonathan Doman auto* propMatch = new sdbusplus::bus::match_t(
217ee03a9b5SJonathan Doman *dbusConn,
218ee03a9b5SJonathan Doman sdbusplus::bus::match::rules::sender(service) +
219ee03a9b5SJonathan Doman sdbusplus::bus::match::rules::propertiesChanged(object, interface),
22077b9c478SPatrick Williams [commonPropHandler](sdbusplus::message_t& reply) {
221ee03a9b5SJonathan Doman ChangedPropertiesType changedProps;
222ee03a9b5SJonathan Doman // ignore first param (interface name), it has to be correct
223ee03a9b5SJonathan Doman reply.read(std::string(), changedProps);
224ee03a9b5SJonathan Doman
225ee03a9b5SJonathan Doman DEBUG_PRINT << "PropertiesChanged handled\n";
226ee03a9b5SJonathan Doman commonPropHandler(changedProps);
227ee03a9b5SJonathan Doman });
228ee03a9b5SJonathan Doman
229ee03a9b5SJonathan Doman // Set up a match for the InterfacesAdded signal from the service's
230ee03a9b5SJonathan Doman // ObjectManager. This is useful in the case where the object is not added
231ee03a9b5SJonathan Doman // yet, and when it's added they choose to not emit PropertiesChanged. So in
232ee03a9b5SJonathan Doman // order to see the initial value when it comes, we need to watch this too.
233ee03a9b5SJonathan Doman auto* intfMatch = new sdbusplus::bus::match_t(
234ee03a9b5SJonathan Doman *dbusConn,
235ee03a9b5SJonathan Doman sdbusplus::bus::match::rules::sender(service) +
236ee03a9b5SJonathan Doman sdbusplus::bus::match::rules::interfacesAdded(),
237ee03a9b5SJonathan Doman [object = std::string(object), interface = std::string(interface),
23877b9c478SPatrick Williams commonPropHandler](sdbusplus::message_t& reply) {
239ee03a9b5SJonathan Doman sdbusplus::message::object_path changedObject;
240ee03a9b5SJonathan Doman reply.read(changedObject);
241ee03a9b5SJonathan Doman if (changedObject != object)
242ee03a9b5SJonathan Doman {
243ee03a9b5SJonathan Doman return;
244ee03a9b5SJonathan Doman }
245ee03a9b5SJonathan Doman
246ee03a9b5SJonathan Doman std::vector<std::pair<std::string, ChangedPropertiesType>>
247ee03a9b5SJonathan Doman changedInterfaces;
248ee03a9b5SJonathan Doman reply.read(changedInterfaces);
249ee03a9b5SJonathan Doman
250*1d73dcccSPatrick Williams for (const auto& [changedInterface, changedProps] :
251*1d73dcccSPatrick Williams changedInterfaces)
252ee03a9b5SJonathan Doman {
253ee03a9b5SJonathan Doman if (changedInterface != interface)
254ee03a9b5SJonathan Doman {
255ee03a9b5SJonathan Doman continue;
256ee03a9b5SJonathan Doman }
257ee03a9b5SJonathan Doman
258ee03a9b5SJonathan Doman DEBUG_PRINT << "InterfacesAdded handled\n";
259ee03a9b5SJonathan Doman commonPropHandler(changedProps);
260ee03a9b5SJonathan Doman }
261ee03a9b5SJonathan Doman });
262ee03a9b5SJonathan Doman
263ee03a9b5SJonathan Doman if (propertiesChangedMatch != nullptr)
264ee03a9b5SJonathan Doman {
265ee03a9b5SJonathan Doman *propertiesChangedMatch = propMatch;
266ee03a9b5SJonathan Doman }
267ee03a9b5SJonathan Doman
268ee03a9b5SJonathan Doman if (interfacesAddedMatch != nullptr)
269ee03a9b5SJonathan Doman {
270ee03a9b5SJonathan Doman *interfacesAddedMatch = intfMatch;
271ee03a9b5SJonathan Doman }
272ee03a9b5SJonathan Doman }
273ee03a9b5SJonathan Doman
hostStateSetup(const std::shared_ptr<sdbusplus::asio::connection> & conn)274ee03a9b5SJonathan Doman void hostStateSetup(const std::shared_ptr<sdbusplus::asio::connection>& conn)
275ee03a9b5SJonathan Doman {
276ee03a9b5SJonathan Doman static bool initialized = false;
277ee03a9b5SJonathan Doman if (initialized)
278ee03a9b5SJonathan Doman {
279ee03a9b5SJonathan Doman return;
280ee03a9b5SJonathan Doman }
281ee03a9b5SJonathan Doman
282ee03a9b5SJonathan Doman dbusConn = conn;
283ee03a9b5SJonathan Doman
284ee03a9b5SJonathan Doman // Leak the returned match objects. We want them to run forever.
285ee03a9b5SJonathan Doman subscribeToProperty(
286ee03a9b5SJonathan Doman "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
28733ae81feSJason M. Bills state::Host::interface, "CurrentHostState", updatePowerState);
288ee03a9b5SJonathan Doman subscribeToProperty("xyz.openbmc_project.Host.Misc.Manager",
289ee03a9b5SJonathan Doman "/xyz/openbmc_project/misc/platform_state",
290ee03a9b5SJonathan Doman "xyz.openbmc_project.State.Host.Misc", "CoreBiosDone",
291ee03a9b5SJonathan Doman updateBiosDone);
292511b06c0Skasunath // xyz.openbmc_project.Host.Misc.Manager has Intel specific dependencies.
293511b06c0Skasunath // If it is not available, then we can use the OperatingSystemState in
294511b06c0Skasunath // xyz.openbmc_project.State.OperatingSystem. According to x86-power-control
295511b06c0Skasunath // repo, OperatingSystemState should return "standby" once the POST is
296511b06c0Skasunath // asserted.
2977190f3a3SPotin Lai subscribeToProperty("xyz.openbmc_project.State.Host0",
2987190f3a3SPotin Lai "/xyz/openbmc_project/state/host0",
299511b06c0Skasunath "xyz.openbmc_project.State.OperatingSystem.Status",
300511b06c0Skasunath "OperatingSystemState", updateOsState);
301ee03a9b5SJonathan Doman
302ee03a9b5SJonathan Doman initialized = true;
303ee03a9b5SJonathan Doman }
304ee03a9b5SJonathan Doman
30516a2ced3SJonathan Doman namespace dbus
30616a2ced3SJonathan Doman {
getIOContext()30716a2ced3SJonathan Doman boost::asio::io_context& getIOContext()
30816a2ced3SJonathan Doman {
30916a2ced3SJonathan Doman static boost::asio::io_context ioc;
31016a2ced3SJonathan Doman return ioc;
31116a2ced3SJonathan Doman }
getConnection()31216a2ced3SJonathan Doman std::shared_ptr<sdbusplus::asio::connection> getConnection()
31316a2ced3SJonathan Doman {
31416a2ced3SJonathan Doman static auto conn =
31516a2ced3SJonathan Doman std::make_shared<sdbusplus::asio::connection>(getIOContext());
31616a2ced3SJonathan Doman return conn;
31716a2ced3SJonathan Doman }
31816a2ced3SJonathan Doman } // namespace dbus
319ee03a9b5SJonathan Doman } // namespace cpu_info
320