1 #pragma once
2 
3 #include "dbus_types.hpp"
4 #include "dbus_watcher.hpp"
5 
6 #include <phosphor-logging/log.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <sdbusplus/bus/match.hpp>
9 
10 #include <filesystem>
11 #include <fstream>
12 
13 namespace openpower
14 {
15 namespace pels
16 {
17 
18 /**
19  * @class DataInterface
20  *
21  * A base class for gathering data about the system for use
22  * in PELs. Implemented this way to facilitate mocking.
23  */
24 class DataInterfaceBase
25 {
26   public:
27     DataInterfaceBase() = default;
28     virtual ~DataInterfaceBase() = default;
29     DataInterfaceBase(const DataInterfaceBase&) = default;
30     DataInterfaceBase& operator=(const DataInterfaceBase&) = default;
31     DataInterfaceBase(DataInterfaceBase&&) = default;
32     DataInterfaceBase& operator=(DataInterfaceBase&&) = default;
33 
34     /**
35      * @brief Returns the machine Type/Model
36      *
37      * @return string - The machine Type/Model string
38      */
39     virtual std::string getMachineTypeModel() const = 0;
40 
41     /**
42      * @brief Returns the machine serial number
43      *
44      * @return string - The machine serial number
45      */
46     virtual std::string getMachineSerialNumber() const = 0;
47 
48     /**
49      * @brief Says if the system is managed by a hardware
50      *        management console.
51      * @return bool - If the system is HMC managed
52      */
53     virtual bool isHMCManaged() const
54     {
55         return _hmcManaged;
56     }
57 
58     /**
59      * @brief Says if the host is up and running
60      *
61      * @return bool - If the host is running
62      */
63     virtual bool isHostUp() const
64     {
65         return _hostUp;
66     }
67 
68     using HostStateChangeFunc = std::function<void(bool)>;
69 
70     /**
71      * @brief Register a callback function that will get
72      *        called on all host on/off transitions.
73      *
74      * The void(bool) function will get passed the new
75      * value of the host state.
76      *
77      * @param[in] name - The subscription name
78      * @param[in] func - The function to run
79      */
80     void subscribeToHostStateChange(const std::string& name,
81                                     HostStateChangeFunc func)
82     {
83         _hostChangeCallbacks[name] = func;
84     }
85 
86     /**
87      * @brief Unsubscribe from host state changes.
88      *
89      * @param[in] name - The subscription name
90      */
91     void unsubscribeFromHostStateChange(const std::string& name)
92     {
93         _hostChangeCallbacks.erase(name);
94     }
95 
96     /**
97      * @brief Returns the BMC firmware version
98      *
99      * @return std::string - The BMC version
100      */
101     virtual std::string getBMCFWVersion() const
102     {
103         return _bmcFWVersion;
104     }
105 
106     /**
107      * @brief Returns the server firmware version
108      *
109      * @return std::string - The server firmware version
110      */
111     virtual std::string getServerFWVersion() const
112     {
113         return _serverFWVersion;
114     }
115 
116     /**
117      * @brief Returns the BMC FW version ID
118      *
119      * @return std::string - The BMC FW version ID
120      */
121     virtual std::string getBMCFWVersionID() const
122     {
123         return _bmcFWVersionID;
124     }
125 
126     /**
127      * @brief Returns the process name given its PID.
128      *
129      * @param[in] pid - The PID value as a string
130      *
131      * @return std::optional<std::string> - The name, or std::nullopt
132      */
133     std::optional<std::string> getProcessName(const std::string& pid) const
134     {
135         namespace fs = std::filesystem;
136 
137         fs::path path{"/proc"};
138         path /= fs::path{pid} / "exe";
139 
140         if (fs::exists(path))
141         {
142             return fs::read_symlink(path);
143         }
144 
145         return std::nullopt;
146     }
147 
148     /**
149      * @brief Returns the time the system was running.
150      *
151      * @return std::optional<uint64_t> - The System uptime or std::nullopt
152      */
153     std::optional<uint64_t> getUptimeInSeconds() const
154     {
155         std::ifstream versionFile{"/proc/uptime"};
156         std::string line{};
157 
158         std::getline(versionFile, line);
159         auto pos = line.find(" ");
160         if (pos == std::string::npos)
161         {
162             return std::nullopt;
163         }
164 
165         uint64_t seconds = atol(line.substr(0, pos).c_str());
166         if (seconds == 0)
167         {
168             return std::nullopt;
169         }
170 
171         return seconds;
172     }
173 
174     /**
175      * @brief Returns the time the system was running.
176      *
177      * @param[in] seconds - The number of seconds the system has been running
178      *
179      * @return std::string - days/hours/minutes/seconds
180      */
181     std::string getBMCUptime(uint64_t seconds) const
182     {
183         time_t t(seconds);
184         tm* p = gmtime(&t);
185 
186         std::string uptime = std::to_string(p->tm_year - 70) + "y " +
187                              std::to_string(p->tm_yday) + "d " +
188                              std::to_string(p->tm_hour) + "h " +
189                              std::to_string(p->tm_min) + "m " +
190                              std::to_string(p->tm_sec) + "s";
191 
192         return uptime;
193     }
194 
195     /**
196      * @brief Returns the system load average over the past 1 minute, 5 minutes
197      *        and 15 minutes.
198      *
199      * @return std::string - The system load average
200      */
201     std::string getBMCLoadAvg() const
202     {
203         std::string loadavg{};
204 
205         std::ifstream loadavgFile{"/proc/loadavg"};
206         std::string line;
207         std::getline(loadavgFile, line);
208 
209         size_t count = 3;
210         for (size_t i = 0; i < count; i++)
211         {
212             auto pos = line.find(" ");
213             if (pos == std::string::npos)
214             {
215                 return {};
216             }
217 
218             if (i != count - 1)
219             {
220                 loadavg.append(line.substr(0, pos + 1));
221             }
222             else
223             {
224                 loadavg.append(line.substr(0, pos));
225             }
226 
227             line = line.substr(pos + 1);
228         }
229 
230         return loadavg;
231     }
232 
233     /**
234      * @brief Returns the 'send event logs to host' setting.
235      *
236      * @return bool - If sending PELs to the host is enabled.
237      */
238     virtual bool getHostPELEnablement() const
239     {
240         return _sendPELsToHost;
241     }
242 
243     /**
244      * @brief Returns the BMC state
245      *
246      * @return std::string - The BMC state property value
247      */
248     virtual std::string getBMCState() const
249     {
250         return _bmcState;
251     }
252 
253     /**
254      * @brief Returns the Chassis state
255      *
256      * @return std::string - The chassis state property value
257      */
258     virtual std::string getChassisState() const
259     {
260         return _chassisState;
261     }
262 
263     /**
264      * @brief Returns the chassis requested power
265      *        transition value.
266      *
267      * @return std::string - The chassis transition property
268      */
269     virtual std::string getChassisTransition() const
270     {
271         return _chassisTransition;
272     }
273 
274     /**
275      * @brief Returns the Host state
276      *
277      * @return std::string - The Host state property value
278      */
279     virtual std::string getHostState() const
280     {
281         return _hostState;
282     }
283 
284     /**
285      * @brief Returns the Boot state
286      *
287      * @return std::string - The Boot state property value
288      */
289     virtual std::string getBootState() const
290     {
291         return _bootState;
292     }
293 
294     /**
295      * @brief Returns the motherboard CCIN
296      *
297      * @return std::string The motherboard CCIN
298      */
299     virtual std::string getMotherboardCCIN() const = 0;
300 
301     /**
302      * @brief Returns the system IM
303      *
304      * @return std::string The system IM
305      */
306     virtual std::vector<uint8_t> getSystemIMKeyword() const = 0;
307 
308     /**
309      * @brief Get the fields from the inventory necessary for doing
310      *        a callout on an inventory path.
311      *
312      * @param[in] inventoryPath - The item to get the data for
313      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
314      * @param[out] ccin - Filled in with the VINI/CC keyword
315      * @param[out] serialNumber - Filled in with the VINI/SN keyword
316      */
317     virtual void getHWCalloutFields(const std::string& inventoryPath,
318                                     std::string& fruPartNumber,
319                                     std::string& ccin,
320                                     std::string& serialNumber) const = 0;
321 
322     /**
323      * @brief Get the location code for an inventory item.
324      *
325      * @param[in] inventoryPath - The item to get the data for
326      *
327      * @return std::string - The location code
328      */
329     virtual std::string
330         getLocationCode(const std::string& inventoryPath) const = 0;
331 
332     /**
333      * @brief Get the list of system type names the system is called.
334      *
335      * @return std::vector<std::string> - The list of names
336      */
337     virtual std::vector<std::string> getSystemNames() const = 0;
338 
339     /**
340      * @brief Fills in the placeholder 'Ufcs' in the passed in location
341      *        code with the machine feature code and serial number, which
342      *        is needed to create a valid location code.
343      *
344      * @param[in] locationCode - Location code value starting with Ufcs-, and
345      *                           if that isn't present it will be added first.
346      *
347      * @param[in] node - The node number the location is on.
348      *
349      * @return std::string - The expanded location code
350      */
351     virtual std::string expandLocationCode(const std::string& locationCode,
352                                            uint16_t node) const = 0;
353 
354     /**
355      * @brief Returns the inventory path for the FRU that the location
356      *        code represents.
357      *
358      * @param[in] locationCode - If an expanded location code, then the
359      *                           full location code.
360      *                           If not expanded, a location code value
361      *                           starting with Ufcs-, and if that isn't
362      *                           present it will be added first.
363      *
364      * @param[in] node - The node number the location is on.  Ignored if the
365      *                   expanded location code is passed in.
366      *
367      * @param[in] expanded - If the location code already has the relevent
368      *                       VPD fields embedded in it.
369      *
370      * @return std::string - The inventory D-Bus object
371      */
372     virtual std::string getInventoryFromLocCode(const std::string& LocationCode,
373                                                 uint16_t node,
374                                                 bool expanded) const = 0;
375 
376     /**
377      * @brief Sets the Asserted property on the LED group passed in.
378      *
379      * @param[in] ledGroup - The LED group D-Bus path
380      * @param[in] value - The value to set it to
381      */
382     virtual void assertLEDGroup(const std::string& ledGroup,
383                                 bool value) const = 0;
384 
385     /**
386      * @brief Sets the Functional property on the OperationalStatus
387      *        interface on a D-Bus object.
388      *
389      * @param[in] objectPath - The D-Bus object path
390      * @param[in] functional - The value
391      */
392     virtual void setFunctional(const std::string& objectPath,
393                                bool functional) const = 0;
394 
395     /**
396      * @brief Sets the critical association on the D-Bus object.
397      *
398      * @param[in] objectPath - The D-Bus object path
399      */
400     virtual void
401         setCriticalAssociation(const std::string& objectPath) const = 0;
402 
403     /**
404      * @brief Returns the manufacturing QuiesceOnError property
405      *
406      * @return bool - Manufacturing QuiesceOnError property
407      */
408     virtual bool getQuiesceOnError() const = 0;
409 
410     /**
411      * @brief Split location code into base and connector segments
412      *
413      * A location code that ends in '-Tx', where 'x' is a number,
414      * represents a connector, such as a USB cable connector.
415      *
416      * This function splits the passed in location code into a
417      * base and connector segment.  e.g.:
418      *   P0-T1 -> ['P0', '-T1']
419      *   P0 -> ['P0', '']
420      *
421      * @param[in] locationCode - location code to split
422      * @return pair<string, string> - The base and connector segments
423      */
424     static std::pair<std::string, std::string>
425         extractConnectorFromLocCode(const std::string& locationCode);
426 
427     /**
428      * @brief Returns the dump status
429      *
430      * @return bool dump status
431      */
432     virtual std::vector<bool>
433         checkDumpStatus(const std::vector<std::string>& type) const = 0;
434 
435     /**
436      * @brief Create guard record
437      *
438      *  @param[in] binPath: phal devtree binary path used as key
439      *  @param[in] type: Guard type
440      *  @param[in] logPath: error log entry object path
441      */
442     virtual void createGuardRecord(const std::vector<uint8_t>& binPath,
443                                    const std::string& type,
444                                    const std::string& logPath) const = 0;
445 
446     /**
447      * @brief Create Progress SRC property on the boot progress
448      *        interface on a D-Bus object.
449      *
450      * @param[in] priSRC - Primary SRC value (e.g. BD8D1001)
451      * @param[in] srcStruct - Full SRC base structure
452      */
453     virtual void
454         createProgressSRC(const uint64_t& priSRC,
455                           const std::vector<uint8_t>& srcStruct) const = 0;
456 
457     /**
458      * @brief Get the list of unresolved OpenBMC event log ids that have an
459      * associated hardware isolation entry.
460      *
461      * @return std::vector<uint32_t> - The list of log ids
462      */
463     virtual std::vector<uint32_t> getLogIDWithHwIsolation() const = 0;
464 
465     /**
466      * @brief Returns the latest raw progress SRC from the State.Boot.Raw
467      *        D-Bus interface.
468      *
469      * @return std::vector<uint8_t> - The progress SRC bytes
470      */
471     virtual std::vector<uint8_t> getRawProgressSRC() const = 0;
472 
473   protected:
474     /**
475      * @brief Sets the host on/off state and runs any
476      *        callback functions (if there was a change).
477      */
478     void setHostUp(bool hostUp)
479     {
480         if (_hostUp != hostUp)
481         {
482             _hostUp = hostUp;
483 
484             for (auto& [name, func] : _hostChangeCallbacks)
485             {
486                 try
487                 {
488                     func(_hostUp);
489                 }
490                 catch (const std::exception& e)
491                 {
492                     using namespace phosphor::logging;
493                     log<level::ERR>("A host state change callback threw "
494                                     "an exception");
495                 }
496             }
497         }
498     }
499 
500     /**
501      * @brief The hardware management console status.  Always kept
502      *        up to date.
503      */
504     bool _hmcManaged = false;
505 
506     /**
507      * @brief The host up status.  Always kept up to date.
508      */
509     bool _hostUp = false;
510 
511     /**
512      * @brief The map of host state change subscriber
513      *        names to callback functions.
514      */
515     std::map<std::string, HostStateChangeFunc> _hostChangeCallbacks;
516 
517     /**
518      * @brief The BMC firmware version string
519      */
520     std::string _bmcFWVersion;
521 
522     /**
523      * @brief The server firmware version string
524      */
525     std::string _serverFWVersion;
526 
527     /**
528      * @brief The BMC firmware version ID string
529      */
530     std::string _bmcFWVersionID;
531 
532     /**
533      * @brief If sending PELs is enabled.
534      *
535      * This is usually set to false in manufacturing test.
536      */
537     bool _sendPELsToHost = true;
538 
539     /**
540      * @brief The BMC state property
541      */
542     std::string _bmcState;
543 
544     /**
545      * @brief The Chassis current power state property
546      */
547     std::string _chassisState;
548 
549     /**
550      * @brief The Chassis requested power transition property
551      */
552     std::string _chassisTransition;
553 
554     /**
555      * @brief The host state property
556      */
557     std::string _hostState;
558 
559     /**
560      * @brief The boot state property
561      */
562     std::string _bootState;
563 };
564 
565 /**
566  * @class DataInterface
567  *
568  * Concrete implementation of DataInterfaceBase.
569  */
570 class DataInterface : public DataInterfaceBase
571 {
572   public:
573     DataInterface() = delete;
574     ~DataInterface() = default;
575     DataInterface(const DataInterface&) = default;
576     DataInterface& operator=(const DataInterface&) = default;
577     DataInterface(DataInterface&&) = default;
578     DataInterface& operator=(DataInterface&&) = default;
579 
580     /**
581      * @brief Constructor
582      *
583      * @param[in] bus - The sdbusplus bus object
584      */
585     explicit DataInterface(sdbusplus::bus_t& bus);
586 
587     /**
588      * @brief Finds the D-Bus service name that hosts the
589      *        passed in path and interface.
590      *
591      * @param[in] objectPath - The D-Bus object path
592      * @param[in] interface - The D-Bus interface
593      */
594     DBusService getService(const std::string& objectPath,
595                            const std::string& interface) const;
596 
597     /**
598      * @brief Wrapper for the 'GetAll' properties method call
599      *
600      * @param[in] service - The D-Bus service to call it on
601      * @param[in] objectPath - The D-Bus object path
602      * @param[in] interface - The interface to get the props on
603      *
604      * @return DBusPropertyMap - The property results
605      */
606     DBusPropertyMap getAllProperties(const std::string& service,
607                                      const std::string& objectPath,
608                                      const std::string& interface) const;
609     /**
610      * @brief Wrapper for the 'Get' properties method call
611      *
612      * @param[in] service - The D-Bus service to call it on
613      * @param[in] objectPath - The D-Bus object path
614      * @param[in] interface - The interface to get the property on
615      * @param[in] property - The property name
616      * @param[out] value - Filled in with the property value.
617      */
618     void getProperty(const std::string& service, const std::string& objectPath,
619                      const std::string& interface, const std::string& property,
620                      DBusValue& value) const;
621     /**
622      * @brief Returns the machine Type/Model
623      *
624      * @return string - The machine Type/Model string
625      */
626     std::string getMachineTypeModel() const override;
627 
628     /**
629      * @brief Returns the machine serial number
630      *
631      * @return string - The machine serial number
632      */
633     std::string getMachineSerialNumber() const override;
634 
635     /**
636      * @brief Returns the motherboard CCIN
637      *
638      * @return std::string The motherboard CCIN
639      */
640     std::string getMotherboardCCIN() const override;
641 
642     /**
643      * @brief Returns the system IM
644      *
645      * @return std::vector The system IM keyword in 4 byte vector
646      */
647     std::vector<uint8_t> getSystemIMKeyword() const override;
648 
649     /**
650      * @brief Get the fields from the inventory necessary for doing
651      *        a callout on an inventory path.
652      *
653      * @param[in] inventoryPath - The item to get the data for
654      * @param[out] fruPartNumber - Filled in with the VINI/FN keyword
655      * @param[out] ccin - Filled in with the VINI/CC keyword
656      * @param[out] serialNumber - Filled in with the VINI/SN keyword
657      */
658     void getHWCalloutFields(const std::string& inventoryPath,
659                             std::string& fruPartNumber, std::string& ccin,
660                             std::string& serialNumber) const override;
661 
662     /**
663      * @brief Get the location code for an inventory item.
664      *
665      * Throws an exception if the inventory item doesn't have the
666      * location code interface.
667      *
668      * @param[in] inventoryPath - The item to get the data for
669      *
670      * @return std::string - The location code
671      */
672     std::string
673         getLocationCode(const std::string& inventoryPath) const override;
674 
675     /**
676      * @brief Get the list of system type names the system is called.
677      *
678      * @return std::vector<std::string> - The list of names
679      */
680     std::vector<std::string> getSystemNames() const override;
681 
682     /**
683      * @brief Fills in the placeholder 'Ufcs' in the passed in location
684      *        code with the machine feature code and serial number, which
685      *        is needed to create a valid location code.
686      *
687      * @param[in] locationCode - Location code value starting with Ufcs-, and
688      *                           if that isn't present it will be added first.
689      *
690      * @param[in] node - The node number the location is one.
691      *
692      * @return std::string - The expanded location code
693      */
694     std::string expandLocationCode(const std::string& locationCode,
695                                    uint16_t node) const override;
696 
697     /**
698      * @brief Returns the inventory path for the FRU that the location
699      *        code represents.
700      *
701      * @param[in] locationCode - If an expanded location code, then the
702      *                           full location code.
703      *                           If not expanded, a location code value
704      *                           starting with Ufcs-, and if that isn't
705      *                           present it will be added first.
706      *
707      * @param[in] node - The node number the location is on.  Ignored if the
708      *                   expanded location code is passed in.
709      *
710      * @param[in] expanded - If the location code already has the relevent
711      *                       VPD fields embedded in it.
712      *
713      * @return std::string - The inventory D-Bus object
714      */
715     std::string getInventoryFromLocCode(const std::string& locationCode,
716                                         uint16_t node,
717                                         bool expanded) const override;
718 
719     /**
720      * @brief Sets the Asserted property on the LED group passed in.
721      *
722      * @param[in] ledGroup - The LED group D-Bus path
723      * @param[in] value - The value to set it to
724      */
725     void assertLEDGroup(const std::string& ledGroup, bool value) const override;
726 
727     /**
728      * @brief Sets the Functional property on the OperationalStatus
729      *        interface on a D-Bus object.
730      *
731      * @param[in] objectPath - The D-Bus object path
732      * @param[in] functional - The value
733      */
734     void setFunctional(const std::string& objectPath,
735                        bool functional) const override;
736 
737     /**
738      * @brief Sets the critical association on the D-Bus object.
739      *
740      * @param[in] objectPath - The D-Bus object path
741      */
742     void setCriticalAssociation(const std::string& objectPath) const override;
743 
744     /**
745      * @brief Returns the manufacturing QuiesceOnError property
746      *
747      * @return bool - Manufacturing QuiesceOnError property
748      */
749     bool getQuiesceOnError() const override;
750 
751     /**
752      * @brief Returns the dump status
753      *
754      * @param[in] type - The dump type to check for
755      *
756      * @return bool dump status
757      */
758     std::vector<bool>
759         checkDumpStatus(const std::vector<std::string>& type) const override;
760 
761     /**
762      * @brief Create guard record
763      *
764      *  @param[in] binPath: phal devtree binary path used as key
765      *  @param[in] type: Guard type
766      *  @param[in] logPath: error log entry object path
767      */
768     void createGuardRecord(const std::vector<uint8_t>& binPath,
769                            const std::string& type,
770                            const std::string& logPath) const override;
771 
772     /**
773      * @brief Create Progress SRC property on the boot progress
774      *        interface on a D-Bus object.
775      *
776      * @param[in] priSRC - Primary SRC value
777      * @param[in] srcStruct - Full SRC base structure
778      */
779     void
780         createProgressSRC(const uint64_t& priSRC,
781                           const std::vector<uint8_t>& srcStruct) const override;
782 
783     /**
784      * @brief Get the list of unresolved OpenBMC event log ids that have an
785      * associated hardware isolation entry.
786      *
787      * @return std::vector<uint32_t> - The list of log ids
788      */
789     std::vector<uint32_t> getLogIDWithHwIsolation() const override;
790 
791     /**
792      * @brief Returns the latest raw progress SRC from the State.Boot.Raw
793      *        D-Bus interface.
794      *
795      * @return std::vector<uint8_t>: The progress SRC bytes
796      */
797     std::vector<uint8_t> getRawProgressSRC() const override;
798 
799   private:
800     /**
801      * @brief Reads the BMC firmware version string and puts it into
802      *        _bmcFWVersion.
803      */
804     void readBMCFWVersion();
805 
806     /**
807      * @brief Reads the server firmware version string and puts it into
808      *        _serverFWVersion.
809      */
810     void readServerFWVersion();
811 
812     /**
813      * @brief Reads the BMC firmware version ID and puts it into
814      *        _bmcFWVersionID.
815      */
816     void readBMCFWVersionID();
817 
818     /**
819      * @brief Finds all D-Bus paths that contain any of the interfaces
820      *        passed in, by using GetSubTreePaths.
821      *
822      * @param[in] interfaces - The desired interfaces
823      *
824      * @return The D-Bus paths.
825      */
826     DBusPathList getPaths(const DBusInterfaceList& interfaces) const;
827 
828     /**
829      * @brief The interfacesAdded callback used on the inventory to
830      *        find the D-Bus object that has the motherboard interface.
831      *        When the motherboard is found, it then adds a PropertyWatcher
832      *        for the motherboard CCIN.
833      */
834     void motherboardIfaceAdded(sdbusplus::message_t& msg);
835 
836     /**
837      * @brief Adds the Ufcs- prefix to the location code passed in
838      *        if necessary.
839      *
840      * Needed because the location codes that come back from the
841      * message registry and device callout JSON don't have it.
842      *
843      * @param[in] - The location code without a prefix, like P1-C1
844      *
845      * @return std::string - The location code with the prefix
846      */
847     static std::string addLocationCodePrefix(const std::string& locationCode);
848 
849     /**
850      * @brief The D-Bus property or interface watchers that have callbacks
851      *        registered that will set members in this class when
852      *        they change.
853      */
854     std::vector<std::unique_ptr<DBusWatcher>> _properties;
855 
856     /**
857      * @brief The sdbusplus bus object for making D-Bus calls.
858      */
859     sdbusplus::bus_t& _bus;
860 
861     /**
862      * @brief The interfacesAdded match object used to wait for inventory
863      *        interfaces to show up, so that the object with the motherboard
864      *        interface can be found.  After it is found, this object is
865      *        deleted.
866      */
867     std::unique_ptr<sdbusplus::bus::match_t> _inventoryIfacesAddedMatch;
868 };
869 
870 } // namespace pels
871 } // namespace openpower
872