1 #pragma once
2 
3 #include "occ_pass_through.hpp"
4 #include "occ_status.hpp"
5 #ifdef PLDM
6 #include "pldm.hpp"
7 
8 #include <libphal.H>
9 #endif
10 #include "powercap.hpp"
11 #include "utils.hpp"
12 #ifdef POWER10
13 #include "powermode.hpp"
14 #endif
15 
16 #include <sdbusplus/bus.hpp>
17 #include <sdeventplus/event.hpp>
18 #include <sdeventplus/utility/timer.hpp>
19 
20 #include <cstring>
21 #include <functional>
22 #include <vector>
23 
24 namespace sdbusRule = sdbusplus::bus::match::rules;
25 namespace open_power
26 {
27 namespace occ
28 {
29 
30 #ifdef READ_OCC_SENSORS
31 enum occFruType
32 {
33     processorCore = 0,
34     internalMemCtlr = 1,
35     dimm = 2,
36     memCtrlAndDimm = 3,
37     VRMVdd = 6,
38     PMIC = 7,
39     memCtlrExSensor = 8,
40     processorIoRing = 9
41 };
42 #endif
43 
44 /** @brief Default time, in seconds, between OCC poll commands */
45 #ifndef POWER10
46 constexpr unsigned int defaultPollingInterval = 1;
47 #else
48 constexpr unsigned int defaultPollingInterval = 5;
49 #endif
50 
51 constexpr auto AMBIENT_PATH =
52     "/xyz/openbmc_project/sensors/temperature/Ambient_Virtual_Temp";
53 constexpr auto AMBIENT_INTERFACE = "xyz.openbmc_project.Sensor.Value";
54 constexpr auto AMBIENT_PROP = "Value";
55 constexpr auto ALTITUDE_PATH = "/xyz/openbmc_project/sensors/altitude/Altitude";
56 constexpr auto ALTITUDE_INTERFACE = "xyz.openbmc_project.Sensor.Value";
57 constexpr auto ALTITUDE_PROP = "Value";
58 
59 /** @class Manager
60  *  @brief Builds and manages OCC objects
61  */
62 struct Manager
63 {
64   public:
65     Manager() = delete;
66     Manager(const Manager&) = delete;
67     Manager& operator=(const Manager&) = delete;
68     Manager(Manager&&) = delete;
69     Manager& operator=(Manager&&) = delete;
70     ~Manager() = default;
71 
72     /** @brief Adds OCC pass-through and status objects on the bus
73      *         when corresponding CPU inventory is created.
74      *
75      *  @param[in] event - Unique ptr reference to sd_event
76      */
Manageropen_power::occ::Manager77     explicit Manager(EventPtr& event) :
78         event(event), pollInterval(defaultPollingInterval),
79         sdpEvent(sdeventplus::Event::get_default()),
80         _pollTimer(
81             std::make_unique<
82                 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
83                 sdpEvent, std::bind(&Manager::pollerTimerExpired, this))),
84         ambientPropChanged(
85             utils::getBus(),
86             sdbusRule::member("PropertiesChanged") +
87                 sdbusRule::path(AMBIENT_PATH) +
88                 sdbusRule::argN(0, AMBIENT_INTERFACE) +
89                 sdbusRule::interface("org.freedesktop.DBus.Properties"),
90             std::bind(&Manager::ambientCallback, this, std::placeholders::_1))
91 #ifdef PLDM
92         ,
93         pldmHandle(std::make_unique<pldm::Interface>(
94             std::bind(std::mem_fn(&Manager::updateOCCActive), this,
95                       std::placeholders::_1, std::placeholders::_2),
96             std::bind(std::mem_fn(&Manager::sbeHRESETResult), this,
97                       std::placeholders::_1, std::placeholders::_2),
98             std::bind(std::mem_fn(&Manager::updateOccSafeMode), this,
99                       std::placeholders::_1),
100             event))
101 #endif
102 #ifdef POWER10
103         ,
104         discoverTimer(
105             std::make_unique<
106                 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
107                 sdpEvent, std::bind(&Manager::findAndCreateObjects, this))),
108         waitForAllOccsTimer(
109             std::make_unique<
110                 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
111                 sdpEvent, std::bind(&Manager::occsNotAllRunning, this)))
112 #ifdef PLDM
113         ,
114         throttlePldmTraceTimer(
115             std::make_unique<
116                 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
117                 sdpEvent, std::bind(&Manager::throttlePldmTraceExpired, this)))
118 #endif
119 #endif // POWER10
120     {
121 #ifdef I2C_OCC
122         // I2C OCC status objects are initialized directly
123         initStatusObjects();
124 #else
125         findAndCreateObjects();
126 #endif
127         readAltitude();
128     }
129 
130     /** @brief Return the number of bound OCCs */
getNumOCCsopen_power::occ::Manager131     inline auto getNumOCCs() const
132     {
133         return activeCount;
134     }
135 
136 #ifdef PLDM
137     /** @brief Called by a Device to report that the SBE timed out
138      *         and appropriate action should be taken
139      *
140      * @param[in] instance - the OCC instance id
141      */
142     void sbeTimeout(unsigned int instance);
143 #endif
144 
145     /** @brief Return the latest ambient and altitude readings
146      *
147      *  @param[out] ambientValid - true if ambientTemp is valid
148      *  @param[out] ambient - ambient temperature in degrees C
149      *  @param[out] altitude - altitude in meters
150      */
151     void getAmbientData(bool& ambientValid, uint8_t& ambientTemp,
152                         uint16_t& altitude) const;
153 
154     /** @brief Notify pcap object to update bounds */
155     void updatePcapBounds() const;
156 
157     /**
158      * @brief Set all sensor values of this OCC to NaN.
159      * @param[in] id - Id of the OCC.
160      * */
161     void setSensorValueToNaN(uint32_t id) const;
162 
163     /** @brief Set all sensor values of this OCC to NaN and non functional.
164      *
165      *  @param[in] id - Id of the OCC.
166      */
167     void setSensorValueToNonFunctional(uint32_t id) const;
168 
169   private:
170     /** @brief Creates the OCC D-Bus objects.
171      */
172     void findAndCreateObjects();
173 
174     /** @brief Callback that responds to cpu creation in the inventory -
175      *         by creating the needed objects.
176      *
177      *  @param[in] msg - bus message
178      *
179      *  @returns 0 to indicate success
180      */
181     int cpuCreated(sdbusplus::message_t& msg);
182 
183     /** @brief Create child OCC objects.
184      *
185      *  @param[in] occ - the occ name, such as occ0.
186      */
187     void createObjects(const std::string& occ);
188 
189     /** @brief Callback handler invoked by Status object when the OccActive
190      *         property is changed. This is needed to make sure that the
191      *         error detection is started only after all the OCCs are bound.
192      *         Similarly, when one of the OCC gets its OccActive property
193      *         un-set, then the OCC error detection needs to be stopped on
194      *         all the OCCs
195      *
196      *  @param[in] status - OccActive status
197      */
198     void statusCallBack(instanceID instance, bool status);
199 
200     /** @brief Sends a Heartbeat command to host control command handler */
201     void sendHeartBeat();
202 
203     /** @brief reference to sd_event wrapped in unique_ptr */
204     EventPtr& event;
205 
206     /** @brief OCC pass-through objects */
207     std::vector<std::unique_ptr<PassThrough>> passThroughObjects;
208 
209     /** @brief OCC Status objects */
210     std::vector<std::unique_ptr<Status>> statusObjects;
211 
212     /** @brief Power cap monitor and occ notification object */
213     std::unique_ptr<open_power::occ::powercap::PowerCap> pcap;
214 
215 #ifdef POWER10
216     /** @brief Power mode monitor and notification object */
217     std::unique_ptr<open_power::occ::powermode::PowerMode> pmode;
218 #endif
219 
220     /** @brief sbdbusplus match objects */
221     std::vector<sdbusplus::bus::match_t> cpuMatches;
222 
223     /** @brief Number of OCCs that are bound */
224     uint8_t activeCount = 0;
225 
226     /** @brief Number of seconds between poll commands */
227     uint8_t pollInterval;
228 
229     /** @brief Ambient temperature of the system in degrees C */
230     uint8_t ambient = 0xFF; // default: not available
231 
232     /** @brief Altitude of the system in meters */
233     uint16_t altitude = 0xFFFF; // default: not available
234 
235     /** @brief Poll timer event */
236     sdeventplus::Event sdpEvent;
237 
238     /** @brief Flags to indicate if waiting for all of the OCC active sensors to
239      * come online */
240     bool waitingForAllOccActiveSensors = false;
241 
242     /** @brief Set containing intance numbers of any OCCs that became active
243      *         while waiting for status objects to be created */
244     std::set<uint8_t> queuedActiveState;
245 
246     /**
247      * @brief The timer to be used once the OCC goes active.  When it expires,
248      *        a POLL command will be sent to the OCC and then timer restarted.
249      */
250     std::unique_ptr<
251         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
252         _pollTimer;
253 
254     /** @brief Subscribe to ambient temperature changed events */
255     sdbusplus::bus::match_t ambientPropChanged;
256 
257 #ifdef I2C_OCC
258     /** @brief Init Status objects for I2C OCC devices
259      *
260      * It iterates in /sys/bus/i2c/devices, finds all occ hwmon devices
261      * and creates status objects.
262      */
263     void initStatusObjects();
264 #endif
265 
266 #ifdef PLDM
267     /** @brief Callback handler invoked by the PLDM event handler when state of
268      *         the OCC is toggled by the host. The caller passes the instance
269      *         of the OCC and state of the OCC.
270      *
271      *  @param[in] instance - instance of the OCC
272      *  @param[in] status - true when the OCC goes active and false when the OCC
273      *                      goes inactive
274      *
275      *  @return true if setting the state of OCC is successful and false if it
276      *          fails.
277      */
278     bool updateOCCActive(instanceID instance, bool status);
279 
280     /** @brief Callback handler invoked by the PLDM event handler when mode of
281      *         the OCC SAFE MODE is inacted or cleared.
282      */
283     void updateOccSafeMode(bool safeState);
284 
285     /** @brief Callback handler invoked by PLDM sensor change when
286      *         the HRESET succeeds or fails.
287      *
288      *  @param[in] instance - the SBE instance id
289      *  @param[in] success - true if the HRESET succeeded, otherwise false
290      */
291     void sbeHRESETResult(instanceID instance, bool success);
292 
293     /** @brief Helper function to check whether an SBE dump should be collected
294      *         now.
295      *
296      *  @param[in] instance - the SBE instance id
297      *
298      *  @return true if an SBE dump should be collected and false if not
299      */
300     bool sbeCanDump(unsigned int instance);
301 
302     /** @brief Helper function to set the SBE state through PDBG/PHAL
303      *
304      * @param[in] instance - instance of the SBE
305      * @param[in] state - the state to which the SBE should be set
306      *
307      */
308     void setSBEState(unsigned int instance, enum sbe_state state);
309 
310     /** @brief Helper function to get the SBE instance PDBG processor target
311      *
312      * @param[in] instance - the SBE instance id
313      *
314      * @return a pointer to the PDBG target
315      */
316     struct pdbg_target* getPdbgTarget(unsigned int instance);
317 
318     /** @brief Whether pdbg_targets_init has been called */
319     bool pdbgInitialized = false;
320 
321     std::unique_ptr<pldm::Interface> pldmHandle = nullptr;
322 #endif
323 
324 #ifdef POWER10
325     /**
326      * @brief Timer used when discovering OCCs in /dev.
327      */
328     std::unique_ptr<
329         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
330         discoverTimer;
331 
332     /**
333      * @brief Used when discovering /dev/occ objects to know if
334      *        any were added since the last check.
335      */
336     std::vector<int> prevOCCSearch;
337 
338     /**
339      * @brief Timer used when waiting for OCCs to go active.
340      */
341     std::unique_ptr<
342         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
343         waitForAllOccsTimer;
344 
345 #ifdef PLDM
346     /**
347      * @brief Timer used to throttle PLDM traces when there are problems
348               determining the OCC status via pldm. Used to prevent excessive
349               journal traces.
350      */
351     std::unique_ptr<
352         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
353         throttlePldmTraceTimer;
354     /**
355      * @brief onPldmTimeoutCreatePel flag will be used to indicate if
356      *        a PEL should get created when the throttlePldmTraceTimer expires.
357      *        The first time the throttlePldmTraceTimer expires, the traces
358      *        will be throttled and then the timer gets restarted. The
359      *        next time the timer expires, a PEL will get created.
360      */
361     bool onPldmTimeoutCreatePel = false;
362 
363     /** @brief Check if all of the OCC Active sensors are available and if not
364      * restart the discoverTimer
365      */
366     void throttlePldmTraceExpired();
367 
368     /** @brief Create a PEL when the code is not able to obtain the OCC PDRs
369      * via PLDM. This is called when the throttlePldmTraceTimer expires.
370      */
371     void createPldmSensorPEL();
372 #endif
373 
374     /** @brief Called when code times out waiting for all OCCs to be running or
375      *         after the app is restarted (Status does not callback into
376      * Manager).
377      */
378     void occsNotAllRunning();
379 
380     /** @brief Check if all of the OCC Active sensors are available and if not
381      * restart the discoverTimer
382      */
383     void checkAllActiveSensors();
384 #endif // POWER10
385 
386     /**
387      * @brief Called when poll timer expires and forces a POLL command to the
388      * OCC. The poll timer will then be restarted.
389      * */
390     void pollerTimerExpired();
391 
392     /**
393      * @brief Finds the OCC devices in /dev
394      *
395      * @return The IDs of the OCCs - 0, 1, etc.
396      */
397     std::vector<int> findOCCsInDev();
398 
399 #ifdef READ_OCC_SENSORS
400     /**
401      * @brief Gets the occ sensor values.
402      * @param[in] occ - pointer to OCCs Status object
403      * */
404     void getSensorValues(std::unique_ptr<Status>& occ);
405 
406     /**
407      * @brief Trigger OCC driver to read the temperature sensors.
408      * @param[in] path - path of the OCC sensors.
409      * @param[in] id - Id of the OCC.
410      * */
411     void readTempSensors(const fs::path& path, uint32_t id);
412 
413     /**
414      * @brief Trigger OCC driver to read the power sensors.
415      * @param[in] path - path of the OCC sensors.
416      * @param[in] id - Id of the OCC.
417      * */
418     void readPowerSensors(const fs::path& path, uint32_t id);
419 
420     /** @brief Store the existing OCC sensors on D-BUS */
421     std::map<std::string, uint32_t> existingSensors;
422 
423     /** @brief Get FunctionID from the `powerX_label` file.
424      *  @param[in] value - the value of the `powerX_label` file.
425      *  @returns FunctionID of the power sensors.
426      */
427     std::optional<std::string>
428         getPowerLabelFunctionID(const std::string& value);
429 
430     /** @brief The power sensor names map */
431     const std::map<std::string, std::string> powerSensorName = {
432         {"system", "total_power"}, {"1", "p0_mem_power"},
433         {"2", "p1_mem_power"},     {"3", "p2_mem_power"},
434         {"4", "p3_mem_power"},     {"5", "p0_power"},
435         {"6", "p1_power"},         {"7", "p2_power"},
436         {"8", "p3_power"},         {"9", "p0_cache_power"},
437         {"10", "p1_cache_power"},  {"11", "p2_cache_power"},
438         {"12", "p3_cache_power"},  {"13", "io_a_power"},
439         {"14", "io_b_power"},      {"15", "io_c_power"},
440         {"16", "fans_a_power"},    {"17", "fans_b_power"},
441         {"18", "storage_a_power"}, {"19", "storage_b_power"},
442         {"23", "mem_cache_power"}, {"25", "p0_mem_0_power"},
443         {"26", "p0_mem_1_power"},  {"27", "p0_mem_2_power"},
444         {"35", "pcie_dcm0_power"}, {"36", "pcie_dcm1_power"},
445         {"37", "pcie_dcm2_power"}, {"38", "pcie_dcm3_power"},
446         {"39", "io_dcm0_power"},   {"40", "io_dcm1_power"},
447         {"41", "io_dcm2_power"},   {"42", "io_dcm3_power"},
448         {"43", "avdd_total_power"}};
449 
450     /** @brief The dimm temperature sensor names map  */
451     const std::map<uint32_t, std::string> dimmTempSensorName = {
452         {internalMemCtlr, "_intmb_temp"},
453         {dimm, "_dram_temp"},
454         {memCtrlAndDimm, "_dram_extmb_temp"},
455         {PMIC, "_pmic_temp"},
456         {memCtlrExSensor, "_extmb_temp"}};
457 
458     /** @brief The dimm DVFS temperature sensor names map  */
459     const std::map<uint32_t, std::string> dimmDVFSSensorName = {
460         {internalMemCtlr, "dimm_intmb_dvfs_temp"},
461         {dimm, "dimm_dram_dvfs_temp"},
462         {memCtrlAndDimm, "dimm_dram_extmb_dvfs_temp"},
463         {PMIC, "dimm_pmic_dvfs_temp"},
464         {memCtlrExSensor, "dimm_extmb_dvfs_temp"}};
465 #endif
466 
467     /** @brief Read the altitude from DBus */
468     void readAltitude();
469 
470     /** @brief Callback function when ambient temperature changes
471      *
472      *  @param[in]  msg - Data associated with subscribed signal
473      */
474     void ambientCallback(sdbusplus::message_t& msg);
475 
476     /** @brief Confirm that a single OCC master was found and start presence
477      * monitoring
478      */
479     void validateOccMaster();
480 };
481 
482 } // namespace occ
483 } // namespace open_power
484