xref: /openbmc/smbios-mdr/src/cpuinfo_utils.cpp (revision 1d73dccc89f0bb9d1dce3543e5af6b3e3087d5f4)
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