xref: /openbmc/s2600wf-misc/hsbp-manager/src/hsbp_manager.cpp (revision 69ae808e213f2e3ce5a0fc7e805820026065064c)
1 /*
2 // Copyright (c) 2019 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "utils.hpp"
18 
19 #include <boost/algorithm/string/replace.hpp>
20 #include <boost/asio/posix/stream_descriptor.hpp>
21 #include <boost/asio/steady_timer.hpp>
22 #include <boost/container/flat_set.hpp>
23 #include <gpiod.hpp>
24 #include <sdbusplus/asio/connection.hpp>
25 #include <sdbusplus/asio/object_server.hpp>
26 #include <sdbusplus/bus/match.hpp>
27 
28 #include <algorithm>
29 #include <bitset>
30 #include <filesystem>
31 #include <forward_list>
32 #include <fstream>
33 #include <iostream>
34 #include <list>
35 #include <string>
36 #include <utility>
37 
38 extern "C"
39 {
40 #include <i2c/smbus.h>
41 #include <linux/i2c-dev.h>
42 }
43 
44 /****************************************************************************/
45 /******************** Global Constants/Type Declarations ********************/
46 /****************************************************************************/
47 constexpr const char* hsbpCpldInft =
48     "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
49 constexpr const char* hsbpConfigIntf =
50     "xyz.openbmc_project.Configuration.HSBPConfiguration";
51 constexpr const char* nvmeIntf = "xyz.openbmc_project.Inventory.Item.NVMe";
52 constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
53 
54 constexpr size_t scanRateSeconds = 5;
55 constexpr size_t maxDrives = 8; // only 1 byte alloted
56 
57 using NvmeMapping = std::vector<std::string>;
58 /***************************** End of Section *******************************/
59 
60 /****************************************************************************/
61 /**************************** Enums Definitions *****************************/
62 /****************************************************************************/
63 enum class AppState : uint8_t
64 {
65     idle,
66     loadingHsbpConfig,
67     hsbpConfigLoaded,
68     loadingComponents,
69     componentsLoaded,
70     loadingBackplanes,
71     backplanesLoaded,
72     loadingDrives,
73     drivesLoaded
74 };
75 
76 enum class BlinkPattern : uint8_t
77 {
78     off = 0x0,
79     error = 0x2,
80     terminate = 0x3
81 };
82 /***************************** End of Section *******************************/
83 
84 /****************************************************************************/
85 /************ HSBP Configuration related struct/class Definitions ***********/
86 /****************************************************************************/
87 struct HsbpConfig
88 {
89     size_t rootBus;
90     std::vector<std::string> supportedHsbps;
91     std::unordered_map<std::string, NvmeMapping> hsbpNvmeMap;
92     std::vector<std::string> clockBufferTypes;
93     std::vector<std::string> ioExpanderTypes;
94 
clearConfigHsbpConfig95     void clearConfig()
96     {
97         rootBus = -1;
98         supportedHsbps.clear();
99         hsbpNvmeMap.clear();
100         clockBufferTypes.clear();
101         ioExpanderTypes.clear();
102     }
103 };
104 
105 class ClockBuffer
106 {
107     size_t bus;
108     size_t address;
109     std::string modeOfOperation;
110     size_t outCtrlBaseAddr;
111     size_t outCtrlByteCount;
112     std::unordered_map<std::string, std::vector<std::string>> byteMap;
113     std::string name;
114     std::string type;
115 
116     int file = -1;
117     bool initialized = false;
118 
initialize()119     void initialize()
120     {
121         /* Execute below operation only when mode of operation is SMBus. By
122          * default the clock buffer is configured to follow OE pin output, so we
123          * need to set the output value to 0 to disable the clock outputs and 1
124          * to enable clock output. If mode of operation is IO, then the IO value
125          * will determine the disable/enable of clock output */
126         if (modeOfOperation == "SMBus")
127         {
128             if (file < 0)
129             {
130                 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
131                             O_RDWR | O_CLOEXEC);
132                 if (file < 0)
133                 {
134                     std::cerr << "ClockBuffer : \"" << name
135                               << "\" - Unable to open bus : " << bus << "\n";
136                     return;
137                 }
138             }
139 
140             if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
141             {
142                 std::cerr << "ClockBuffer : \"" << name
143                           << "\" - Unable to set address to " << address
144                           << "\n";
145                 return;
146             }
147 
148             for (uint8_t i = 0; i < outCtrlByteCount; i++)
149             {
150                 std::string byteName = "Byte" + std::to_string(i);
151 
152                 auto byte = byteMap.find(byteName);
153                 if (byte == byteMap.end())
154                 {
155                     std::cerr << "ClockBuffer : \"" << name
156                               << "\" - Byte map error ! Unable to find "
157                               << byteName << "\n";
158                     return;
159                 }
160 
161                 /* Get current value of output control register */
162                 int read = i2c_smbus_read_byte_data(
163                     file, static_cast<uint8_t>(outCtrlBaseAddr + i));
164                 if (read < 0)
165                 {
166                     std::cerr << "ClockBuffer : \"" << name
167                               << "\" - Error: Unable to read data from clock "
168                                  "buffer register\n";
169                     return;
170                 }
171 
172                 std::bitset<8> currByte(read);
173                 bool writeRequired = false;
174 
175                 /* Set 0/1 only at bit position that we have a NVMe drive (i.e.
176                  * ignore where byteMap is "-"). We do not want to touch other
177                  * bits */
178                 for (uint8_t bit = 0; bit < 8; bit++)
179                 {
180                     if (byte->second.at(bit) != "-")
181                     {
182                         writeRequired = true;
183                         /* Default to enabling the clock output and once the
184                          * HSBP's are detected the clocks will be
185                          * enabled/disabled depending on the drive status */
186                         /* TODO: This code might require a re-visit in case of
187                          * any signal integrity issues in the future */
188                         currByte.set(bit);
189                     }
190                 }
191 
192                 if (writeRequired)
193                 {
194                     int ret = i2c_smbus_write_byte_data(
195                         file, static_cast<uint8_t>(outCtrlBaseAddr + i),
196                         static_cast<uint8_t>(currByte.to_ulong()));
197 
198                     if (ret < 0)
199                     {
200                         std::cerr
201                             << "ClockBuffer : \"" << name
202                             << "\" - Error: Unable to write data to clock "
203                                "buffer register\n";
204                         return;
205                     }
206                 }
207             }
208         }
209         initialized = true;
210         std::cerr << "ClockBuffer : \"" << name << "\" initialized\n";
211     }
212 
213   public:
ClockBuffer(size_t busIn,size_t addressIn,std::string & modeOfOperationIn,size_t outCtrlBaseAddrIn,size_t outCtrlByteCountIn,std::unordered_map<std::string,std::vector<std::string>> & byteMapIn,std::string & nameIn,std::string & typeIn)214     ClockBuffer(
215         size_t busIn, size_t addressIn, std::string& modeOfOperationIn,
216         size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
217         std::unordered_map<std::string, std::vector<std::string>>& byteMapIn,
218         std::string& nameIn, std::string& typeIn) :
219         bus(busIn), address(addressIn),
220         modeOfOperation(std::move(modeOfOperationIn)),
221         outCtrlBaseAddr(outCtrlBaseAddrIn),
222         outCtrlByteCount(outCtrlByteCountIn), byteMap(std::move(byteMapIn)),
223         name(std::move(nameIn)), type(std::move(typeIn))
224     {
225         initialize();
226     }
227 
isInitialized()228     bool isInitialized()
229     {
230         if (!initialized)
231         {
232             /* There was an issue with the initialization of this component. Try
233              * to invoke initialization again */
234             initialize();
235         }
236         return initialized;
237     }
238 
getName()239     std::string getName()
240     {
241         return name;
242     }
243 
enableDisableClock(std::forward_list<std::string> & nvmeDrivesInserted,std::forward_list<std::string> & nvmeDrivesRemoved)244     bool enableDisableClock(std::forward_list<std::string>& nvmeDrivesInserted,
245                             std::forward_list<std::string>& nvmeDrivesRemoved)
246     {
247         if (modeOfOperation != "SMBus")
248         {
249             /* The clock is enabled using IO expander. No action needed from
250              * here */
251             return true;
252         }
253 
254         if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
255         {
256             /* There are no drives to update */
257             return true;
258         }
259 
260         for (uint8_t i = 0; i < outCtrlByteCount; i++)
261         {
262             std::string byteName = "Byte" + std::to_string(i);
263 
264             auto byte = byteMap.find(byteName);
265             if (byte == byteMap.end())
266             {
267                 std::cerr << "ClockBuffer : \"" << name
268                           << "\" - Byte map error ! Unable to find " << byteName
269                           << "\n";
270                 return false;
271             }
272 
273             /* Get current value of output control register */
274             int read = i2c_smbus_read_byte_data(
275                 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
276             if (read < 0)
277             {
278                 std::cerr << "ClockBuffer : \"" << name
279                           << "\" - Error: Unable to read data from clock "
280                              "buffer register\n";
281                 return false;
282             }
283 
284             std::bitset<8> currByte(read);
285             bool writeRequired = false;
286 
287             /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
288              * reset the bit if found in nvmeDrivesRemoved */
289             for (uint8_t bit = 0; bit < 8; bit++)
290             {
291                 /* The remove function returns number of elements removed from
292                  * list indicating the presence of the drive and also removing
293                  * it form the list */
294                 if (nvmeDrivesInserted.remove(byte->second.at(bit)))
295                 {
296                     writeRequired = true;
297                     currByte.set(bit);
298                     continue;
299                 }
300 
301                 if (nvmeDrivesRemoved.remove(byte->second.at(bit)))
302                 {
303                     writeRequired = true;
304                     currByte.reset(bit);
305                 }
306             }
307 
308             if (!writeRequired)
309             {
310                 /* No Write is required as there are no changes */
311                 continue;
312             }
313 
314             int ret = i2c_smbus_write_byte_data(
315                 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
316                 static_cast<uint8_t>(currByte.to_ulong()));
317             if (ret < 0)
318             {
319                 std::cerr << "ClockBuffer : \"" << name
320                           << "\" - Error: Unable to write data to clock "
321                              "buffer register\n";
322                 return false;
323             }
324         }
325 
326         return true;
327     }
328 
~ClockBuffer()329     ~ClockBuffer()
330     {
331         if (file >= 0)
332         {
333             close(file);
334         }
335     }
336 };
337 
338 class IoExpander
339 {
340     size_t bus;
341     size_t address;
342     size_t confIORegAddr;
343     size_t outCtrlBaseAddr;
344     size_t outCtrlByteCount;
345     std::unordered_map<std::string, std::vector<std::string>> ioMap;
346     std::string name;
347     std::string type;
348 
349     int file = -1;
350     bool initialized = false;
351 
initialize()352     void initialize()
353     {
354         /* Initialize the IO expander Control register to configure the IO ports
355          * as outputs and set the output*/
356         if (file < 0)
357         {
358             file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
359                         O_RDWR | O_CLOEXEC);
360             if (file < 0)
361             {
362                 std::cerr << "IoExpander : " << name
363                           << " - Unable to open bus : " << bus << "\n";
364                 return;
365             }
366         }
367 
368         if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
369         {
370             std::cerr << "IoExpander : \"" << name
371                       << "\" - Unable to set address to " << address << "\n";
372             return;
373         }
374 
375         for (uint8_t i = 0; i < outCtrlByteCount; i++)
376         {
377             std::string ioName = "IO" + std::to_string(i);
378 
379             auto io = ioMap.find(ioName);
380             if (io == ioMap.end())
381             {
382                 std::cerr << "IoExpander : \"" << name
383                           << "\" - IO map error ! Unable to find " << ioName
384                           << "\n";
385                 return;
386             }
387 
388             /* Get current value of IO configuration register */
389             int read1 = i2c_smbus_read_byte_data(
390                 file, static_cast<uint8_t>(confIORegAddr + i));
391             if (read1 < 0)
392             {
393                 std::cerr << "IoExpander : \"" << name
394                           << "\" - Error: Unable to read data from io expander "
395                              "IO control register\n";
396                 return;
397             }
398 
399             /* Get current value of IO Ouput register */
400             int read2 = i2c_smbus_read_byte_data(
401                 file, static_cast<uint8_t>(confIORegAddr + i));
402             if (read2 < 0)
403             {
404                 std::cerr << "IoExpander : \"" << name
405                           << "\" - Error: Unable to read data from io expander "
406                              "IO output register\n";
407                 return;
408             }
409 
410             bool writeRequired = false;
411             std::bitset<8> currCtrlVal(read1);
412             std::bitset<8> currOutVal(read2);
413 
414             /* Set 0/1 only at bit position that we have a NVMe drive (i.e.
415              * ignore where ioMap is "-"). We do not want to touch other
416              * bits */
417             for (uint8_t bit = 0; bit < 8; bit++)
418             {
419                 if (io->second.at(bit) != "-")
420                 {
421                     writeRequired = true;
422                     currCtrlVal.reset(bit);
423                     /* Set the output register to drive the OE pin high thereby
424                      * enabling the clock. Default to enabling the clock output
425                      * and once the HSBP's are detected the clocks will be
426                      * enabled/disabled depending on the drive status */
427                     currOutVal.set(bit);
428                 }
429             }
430 
431             if (writeRequired)
432             {
433                 int ret1 = i2c_smbus_write_byte_data(
434                     file, static_cast<uint8_t>(confIORegAddr + i),
435                     static_cast<uint8_t>(currCtrlVal.to_ulong()));
436                 if (ret1 < 0)
437                 {
438                     std::cerr
439                         << "IoExpander : \"" << name
440                         << "\" - Error: Unable to write data to IO expander "
441                            "IO control register\n";
442                     return;
443                 }
444 
445                 int ret2 = i2c_smbus_write_byte_data(
446                     file, static_cast<uint8_t>(outCtrlBaseAddr + i),
447                     static_cast<uint8_t>(currOutVal.to_ulong()));
448                 if (ret2 < 0)
449                 {
450                     std::cerr
451                         << "IoExpander : \"" << name
452                         << "\" - Error: Unable to write data to IO expander "
453                            "IO output register\n";
454                     return;
455                 }
456             }
457         }
458         initialized = true;
459         std::cerr << "IoExpander : \"" << name << "\" initialized\n";
460     }
461 
462   public:
IoExpander(size_t busIn,size_t addressIn,size_t confIORegAddrIn,size_t outCtrlBaseAddrIn,size_t outCtrlByteCountIn,std::unordered_map<std::string,std::vector<std::string>> & ioMapIn,std::string & nameIn,std::string & typeIn)463     IoExpander(
464         size_t busIn, size_t addressIn, size_t confIORegAddrIn,
465         size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
466         std::unordered_map<std::string, std::vector<std::string>>& ioMapIn,
467         std::string& nameIn, std::string& typeIn) :
468         bus(busIn), address(addressIn), confIORegAddr(confIORegAddrIn),
469         outCtrlBaseAddr(outCtrlBaseAddrIn),
470         outCtrlByteCount(outCtrlByteCountIn), ioMap(std::move(ioMapIn)),
471         name(std::move(nameIn)), type(std::move(typeIn))
472     {
473         initialize();
474     }
475 
isInitialized()476     bool isInitialized()
477     {
478         if (!initialized)
479         {
480             /* There was an issue with the initialization of this component. Try
481              * to invoke initialization again */
482             initialize();
483         }
484         return initialized;
485     }
486 
getName()487     std::string getName()
488     {
489         return name;
490     }
491 
enableDisableOuput(std::forward_list<std::string> & nvmeDrivesInserted,std::forward_list<std::string> & nvmeDrivesRemoved)492     bool enableDisableOuput(std::forward_list<std::string>& nvmeDrivesInserted,
493                             std::forward_list<std::string>& nvmeDrivesRemoved)
494     {
495         if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
496         {
497             /* There are no drives to update */
498             return true;
499         }
500 
501         for (uint8_t i = 0; i < outCtrlByteCount; i++)
502         {
503             std::string ioName = "IO" + std::to_string(i);
504 
505             auto io = ioMap.find(ioName);
506             if (io == ioMap.end())
507             {
508                 std::cerr << "IoExpander : \"" << name
509                           << "\" - IO map error ! Unable to find " << ioName
510                           << "\n";
511                 return false;
512             }
513 
514             /* Get current value of IO output register */
515             int read = i2c_smbus_read_byte_data(
516                 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
517             if (read < 0)
518             {
519                 std::cerr << "IoExpander : \"" << name
520                           << "\" - Error: Unable to read data from io expander "
521                              "register\n";
522                 return false;
523             }
524 
525             std::bitset<8> currVal(read);
526             bool writeRequired = false;
527 
528             /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
529              * reset the bit if found in nvmeDrivesRemoved */
530             for (uint8_t bit = 0; bit < 8; bit++)
531             {
532                 /* The remove function returns number of elements removed from
533                  * list indicating the presence of the drive and also removing
534                  * it form the list */
535                 if (nvmeDrivesInserted.remove(io->second.at(bit)))
536                 {
537                     writeRequired = true;
538                     currVal.set(bit);
539                     continue;
540                 }
541 
542                 if (nvmeDrivesRemoved.remove(io->second.at(bit)))
543                 {
544                     writeRequired = true;
545                     currVal.reset(bit);
546                 }
547             }
548 
549             if (!writeRequired)
550             {
551                 /* No Write is required as there are no changes */
552                 continue;
553             }
554 
555             int ret = i2c_smbus_write_byte_data(
556                 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
557                 static_cast<uint8_t>(currVal.to_ulong()));
558             if (ret < 0)
559             {
560                 std::cerr << "IoExpander : \"" << name
561                           << "\" - Error: Unable to write data to IO expander "
562                              "register\n";
563                 return false;
564             }
565         }
566 
567         return true;
568     }
569 
~IoExpander()570     ~IoExpander()
571     {
572         if (file >= 0)
573         {
574             close(file);
575         }
576     }
577 };
578 /***************************** End of Section *******************************/
579 
580 /****************************************************************************/
581 /*********************** Global Variables Declarations **********************/
582 /****************************************************************************/
583 /* State os Application */
584 static AppState appState = AppState::idle;
585 
586 /* Configuration and Components */
587 static HsbpConfig hsbpConfig;
588 std::forward_list<ClockBuffer> clockBuffers;
589 std::forward_list<IoExpander> ioExpanders;
590 
591 /* Boost IO context and Dbus variables */
592 boost::asio::io_context io;
593 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
594 sdbusplus::asio::object_server objServer(conn);
595 
596 /* GPIO Lines and GPIO Event Descriptors */
597 static gpiod::line nvmeLvc3AlertLine;
598 static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
599 /***************************** End of Section *******************************/
600 
601 /****************************************************************************/
602 /********** HSBP Backplane related struct and Global definitions ************/
603 /****************************************************************************/
604 struct Mux
605 {
MuxMux606     Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
607         bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
608     {}
609     size_t bus;
610     size_t address;
611     size_t channels;
612     size_t index;
613 
614     // to sort in the flat set
operator <Mux615     bool operator<(const Mux& rhs) const
616     {
617         return index < rhs.index;
618     }
619 };
620 
621 struct Led : std::enable_shared_from_this<Led>
622 {
623     // led pattern addresses start at 0x10
LedLed624     Led(const std::string& path, size_t index, int fd) :
625         address(static_cast<uint8_t>(index + 0x10)), file(fd),
626         ledInterface(objServer.add_interface(path, ledGroup::interface))
627     {
628         if (index >= maxDrives)
629         {
630             throw std::runtime_error("Invalid drive index");
631         }
632 
633         if (!set(BlinkPattern::off))
634         {
635             std::cerr << "Cannot initialize LED " << path << "\n";
636         }
637     }
638 
639     // this has to be called outside the constructor for shared_from_this to
640     // work
createInterfaceLed641     void createInterface(void)
642     {
643         ledInterface->register_property(
644             ledGroup::asserted, false,
645             [weakRef{weak_from_this()}](const bool req, bool& val) {
646                 auto self = weakRef.lock();
647                 if (!self)
648                 {
649                     return 0;
650                 }
651                 if (req == val)
652                 {
653                     return 1;
654                 }
655 
656                 if (!isPowerOn())
657                 {
658                     std::cerr << "Can't change blink state when power is off\n";
659                     throw std::runtime_error(
660                         "Can't change blink state when power is off");
661                 }
662                 BlinkPattern pattern =
663                     req ? BlinkPattern::error : BlinkPattern::terminate;
664                 if (!self->set(pattern))
665                 {
666                     std::cerr << "Can't change blink pattern\n";
667                     throw std::runtime_error("Cannot set blink pattern");
668                 }
669                 val = req;
670                 return 1;
671             });
672         ledInterface->initialize();
673     }
674 
~LedLed675     virtual ~Led()
676     {
677         objServer.remove_interface(ledInterface);
678     }
679 
setLed680     bool set(BlinkPattern pattern)
681     {
682         int ret = i2c_smbus_write_byte_data(file, address,
683                                             static_cast<uint8_t>(pattern));
684         return ret >= 0;
685     }
686 
687     uint8_t address;
688     int file;
689     std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
690 };
691 
692 struct Drive
693 {
DriveDrive694     Drive(std::string driveName, bool present, bool isOperational, bool nvme,
695           bool rebuilding) : isNvme(nvme), isPresent(present), name(driveName)
696     {
697         constexpr const char* basePath =
698             "/xyz/openbmc_project/inventory/item/drive/";
699         itemIface =
700             objServer.add_interface(basePath + driveName, inventory::interface);
701         itemIface->register_property("Present", isPresent);
702         itemIface->register_property("PrettyName", driveName);
703         itemIface->initialize();
704         operationalIface = objServer.add_interface(
705             itemIface->get_object_path(),
706             "xyz.openbmc_project.State.Decorator.OperationalStatus");
707 
708         operationalIface->register_property(
709             "Functional", isOperational,
710             [this](const bool req, bool& property) {
711                 if (!isPresent)
712                 {
713                     return 0;
714                 }
715                 if (property == req)
716                 {
717                     return 1;
718                 }
719                 property = req;
720                 if (req)
721                 {
722                     clearFailed();
723                     return 1;
724                 }
725                 markFailed();
726                 return 1;
727             });
728 
729         operationalIface->initialize();
730         rebuildingIface = objServer.add_interface(
731             itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
732         rebuildingIface->register_property("Rebuilding", rebuilding);
733         rebuildingIface->initialize();
734         driveIface =
735             objServer.add_interface(itemIface->get_object_path(),
736                                     "xyz.openbmc_project.Inventory.Item.Drive");
737         driveIface->initialize();
738         associations = objServer.add_interface(itemIface->get_object_path(),
739                                                association::interface);
740         associations->register_property("Associations",
741                                         std::vector<Association>{});
742         associations->initialize();
743 
744         if (isPresent && (!isOperational || rebuilding))
745         {
746             markFailed();
747         }
748     }
~DriveDrive749     virtual ~Drive()
750     {
751         objServer.remove_interface(itemIface);
752         objServer.remove_interface(operationalIface);
753         objServer.remove_interface(rebuildingIface);
754         objServer.remove_interface(assetIface);
755         objServer.remove_interface(driveIface);
756         objServer.remove_interface(associations);
757     }
758 
removeAssetDrive759     void removeAsset()
760     {
761         objServer.remove_interface(assetIface);
762         assetIface = nullptr;
763     }
764 
createAssetDrive765     void createAsset(
766         const boost::container::flat_map<std::string, std::string>& data)
767     {
768         if (assetIface != nullptr)
769         {
770             return;
771         }
772         assetIface = objServer.add_interface(
773             itemIface->get_object_path(),
774             "xyz.openbmc_project.Inventory.Decorator.Asset");
775         for (const auto& [key, value] : data)
776         {
777             assetIface->register_property(key, value);
778             if (key == "SerialNumber")
779             {
780                 serialNumber = value;
781                 serialNumberInitialized = true;
782             }
783         }
784         assetIface->initialize();
785     }
786 
markFailedDrive787     void markFailed(void)
788     {
789         // todo: maybe look this up via mapper
790         constexpr const char* globalInventoryPath =
791             "/xyz/openbmc_project/CallbackManager";
792 
793         if (!isPresent)
794         {
795             return;
796         }
797 
798         operationalIface->set_property("Functional", false);
799         std::vector<Association> warning = {
800             {"", "warning", globalInventoryPath}};
801         associations->set_property("Associations", warning);
802         logDriveError("Drive " + name);
803     }
804 
clearFailedDrive805     void clearFailed(void)
806     {
807         operationalIface->set_property("Functional", true);
808         associations->set_property("Associations", std::vector<Association>{});
809     }
810 
setPresentDrive811     void setPresent(bool set)
812     {
813         // nvme drives get detected by their fru
814         if (set == isPresent)
815         {
816             return;
817         }
818         itemIface->set_property("Present", set);
819         isPresent = set;
820     }
821 
logPresentDrive822     void logPresent()
823     {
824         if (isNvme && !serialNumberInitialized)
825         {
826             // wait until NVMe asset is updated to include the serial number
827             // from the NVMe drive
828             return;
829         }
830 
831         if (!isPresent && loggedPresent)
832         {
833             loggedPresent = false;
834             logDeviceRemoved("Drive", name, serialNumber);
835             serialNumber = "N/A";
836             serialNumberInitialized = false;
837             removeAsset();
838         }
839         else if (isPresent && !loggedPresent)
840         {
841             loggedPresent = true;
842             logDeviceAdded("Drive", name, serialNumber);
843         }
844     }
845 
846     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
847     std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
848     std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
849     std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
850     std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
851     std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
852 
853     bool isNvme;
854     bool isPresent;
855     std::string name;
856     std::string serialNumber = "N/A";
857     bool serialNumberInitialized = false;
858     bool loggedPresent = false;
859 };
860 
861 struct Backplane : std::enable_shared_from_this<Backplane>
862 {
BackplaneBackplane863     Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
864               const std::string& nameIn) :
865         bus(busIn), address(addressIn), backplaneIndex(backplaneIndexIn - 1),
866         name(nameIn), timer(boost::asio::steady_timer(io)),
867         muxes(std::make_shared<boost::container::flat_set<Mux>>())
868     {}
populateAssetBackplane869     void populateAsset(const std::string& rootPath, const std::string& busname)
870     {
871         conn->async_method_call(
872             [assetIface{assetInterface}](
873                 const boost::system::error_code ec,
874                 const boost::container::flat_map<
875                     std::string, std::variant<std::string>>& values) mutable {
876                 if (ec)
877                 {
878                     std::cerr
879                         << "Error getting asset tag from HSBP configuration\n";
880 
881                     return;
882                 }
883                 for (const auto& [key, value] : values)
884                 {
885                     const std::string* ptr = std::get_if<std::string>(&value);
886                     if (ptr == nullptr)
887                     {
888                         std::cerr << key << " Invalid type!\n";
889                         continue;
890                     }
891                     assetIface->register_property(key, *ptr);
892                 }
893                 assetIface->initialize();
894             },
895             busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
896             assetTag);
897     }
898 
zeroPadBackplane899     static std::string zeroPad(const uint8_t val)
900     {
901         std::ostringstream version;
902         version << std::setw(2) << std::setfill('0')
903                 << static_cast<size_t>(val);
904         return version.str();
905     }
906 
runBackplane907     void run(const std::string& rootPath, const std::string& busname)
908     {
909         file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
910                     O_RDWR | O_CLOEXEC);
911         if (file < 0)
912         {
913             std::cerr << "unable to open bus " << bus << "\n";
914             return;
915         }
916 
917         if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
918         {
919             std::cerr << "unable to set address to " << address << "\n";
920             return;
921         }
922 
923         if (!getPresent())
924         {
925             std::cerr << "Cannot detect CPLD\n";
926             return;
927         }
928 
929         getBootVer(bootVer);
930         getFPGAVer(fpgaVer);
931         getSecurityRev(securityRev);
932         std::string dbusName = boost::replace_all_copy(name, " ", "_");
933         hsbpItemIface = objServer.add_interface(
934             "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
935             inventory::interface);
936         hsbpItemIface->register_property("Present", true);
937         hsbpItemIface->register_property("PrettyName", name);
938         hsbpItemIface->initialize();
939 
940         storageInterface = objServer.add_interface(
941             hsbpItemIface->get_object_path(),
942             "xyz.openbmc_project.Inventory.Item.StorageController");
943         storageInterface->initialize();
944 
945         assetInterface =
946             objServer.add_interface(hsbpItemIface->get_object_path(), assetTag);
947 
948         versionIface =
949             objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
950                                     "xyz.openbmc_project.Software.Version");
951         versionIface->register_property(
952             "Version", zeroPad(bootVer) + "." + zeroPad(fpgaVer) + "." +
953                            zeroPad(securityRev));
954         versionIface->register_property(
955             "Purpose",
956             std::string(
957                 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
958         versionIface->initialize();
959 
960         activationIface =
961             objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
962                                     "xyz.openbmc_project.Software.Activation");
963         activationIface->register_property(
964             "Activation",
965             std::string(
966                 "xyz.openbmc_project.Software.Activation.Activations.Active"));
967         activationIface->register_property(
968             "RequestedActivation",
969             std::string("xyz.openbmc_project.Software.Activation."
970                         "RequestedActivations.None"));
971         activationIface->initialize();
972 
973         getPresence(presence);
974         getIFDET(ifdet);
975 
976         populateAsset(rootPath, busname);
977 
978         createDrives();
979 
980         runTimer();
981     }
982 
runTimerBackplane983     void runTimer()
984     {
985         timer.expires_after(std::chrono::seconds(scanRateSeconds));
986         timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
987                              boost::system::error_code ec) {
988             auto self = weak.lock();
989             if (!self)
990             {
991                 return;
992             }
993             if (ec == boost::asio::error::operation_aborted)
994             {
995                 // we're being destroyed
996                 return;
997             }
998             else if (ec)
999             {
1000                 std::cerr << "timer error " << ec.message() << "\n";
1001                 return;
1002             }
1003 
1004             if (!isPowerOn())
1005             {
1006                 // can't access hsbp when power is off
1007                 self->runTimer();
1008                 return;
1009             }
1010 
1011             self->getPresence(self->presence);
1012             self->getIFDET(self->ifdet);
1013             self->getFailed(self->failed);
1014             self->getRebuild(self->rebuilding);
1015 
1016             self->updateDrives();
1017             self->runTimer();
1018         });
1019     }
1020 
createDrivesBackplane1021     void createDrives()
1022     {
1023         for (size_t ii = 0; ii < maxDrives; ii++)
1024         {
1025             uint8_t driveSlot = (1 << ii);
1026             bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1027             bool isPresent = isNvme || (presence & driveSlot);
1028             bool isFailed = !isPresent || failed & driveSlot;
1029             bool isRebuilding = !isPresent && (rebuilding & driveSlot);
1030 
1031             // +1 to convert from 0 based to 1 based
1032             std::string driveName = boost::replace_all_copy(name, " ", "_") +
1033                                     "_Drive_" + std::to_string(ii + 1);
1034             Drive& drive = drives.emplace_back(driveName, isPresent, !isFailed,
1035                                                isNvme, isRebuilding);
1036             std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
1037                 drive.itemIface->get_object_path(), ii, file));
1038             led->createInterface();
1039         }
1040     }
1041 
updateDrivesBackplane1042     void updateDrives()
1043     {
1044         size_t ii = 0;
1045 
1046         for (auto it = drives.begin(); it != drives.end(); it++, ii++)
1047         {
1048             uint8_t driveSlot = (1 << ii);
1049             bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1050             bool isPresent = isNvme || (presence & driveSlot);
1051             bool isFailed = !isPresent || (failed & driveSlot);
1052             bool isRebuilding = isPresent && (rebuilding & driveSlot);
1053 
1054             it->isNvme = isNvme;
1055             it->setPresent(isPresent);
1056             it->logPresent();
1057 
1058             it->rebuildingIface->set_property("Rebuilding", isRebuilding);
1059             if (isFailed || isRebuilding)
1060             {
1061                 it->markFailed();
1062             }
1063             else
1064             {
1065                 it->clearFailed();
1066             }
1067         }
1068     }
1069 
getPresentBackplane1070     bool getPresent()
1071     {
1072         present = i2c_smbus_read_byte(file) >= 0;
1073         return present;
1074     }
1075 
getTypeIDBackplane1076     bool getTypeID(uint8_t& val)
1077     {
1078         constexpr uint8_t addr = 2;
1079         int ret = i2c_smbus_read_byte_data(file, addr);
1080         if (ret < 0)
1081         {
1082             std::cerr << "Error " << __FUNCTION__ << "\n";
1083             return false;
1084         }
1085         val = static_cast<uint8_t>(ret);
1086         return true;
1087     }
1088 
getBootVerBackplane1089     bool getBootVer(uint8_t& val)
1090     {
1091         constexpr uint8_t addr = 3;
1092         int ret = i2c_smbus_read_byte_data(file, addr);
1093         if (ret < 0)
1094         {
1095             std::cerr << "Error " << __FUNCTION__ << "\n";
1096             return false;
1097         }
1098         val = static_cast<uint8_t>(ret);
1099         return true;
1100     }
1101 
getFPGAVerBackplane1102     bool getFPGAVer(uint8_t& val)
1103     {
1104         constexpr uint8_t addr = 4;
1105         int ret = i2c_smbus_read_byte_data(file, addr);
1106         if (ret < 0)
1107         {
1108             std::cerr << "Error " << __FUNCTION__ << "\n";
1109             return false;
1110         }
1111         val = static_cast<uint8_t>(ret);
1112         return true;
1113     }
1114 
getSecurityRevBackplane1115     bool getSecurityRev(uint8_t& val)
1116     {
1117         constexpr uint8_t addr = 5;
1118         int ret = i2c_smbus_read_byte_data(file, addr);
1119         if (ret < 0)
1120         {
1121             std::cerr << "Error " << __FUNCTION__ << "\n";
1122             return false;
1123         }
1124         val = static_cast<uint8_t>(ret);
1125         return true;
1126     }
1127 
getPresenceBackplane1128     bool getPresence(uint8_t& val)
1129     {
1130         // NVMe drives do not assert PRSNTn, and as such do not get reported as
1131         // PRESENT in this register
1132 
1133         constexpr uint8_t addr = 8;
1134 
1135         int ret = i2c_smbus_read_byte_data(file, addr);
1136         if (ret < 0)
1137         {
1138             std::cerr << "Error " << __FUNCTION__ << "\n";
1139             return false;
1140         }
1141         // presence is inverted
1142         val = static_cast<uint8_t>(~ret);
1143         return true;
1144     }
1145 
getIFDETBackplane1146     bool getIFDET(uint8_t& val)
1147     {
1148         // This register is a bitmap of parallel GPIO pins connected to the
1149         // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
1150         // IFDETn low when they are inserted into the HSBP.This register, in
1151         // combination with the PRESENCE register, are used by the BMC to detect
1152         // the presence of NVMe drives.
1153 
1154         constexpr uint8_t addr = 9;
1155 
1156         int ret = i2c_smbus_read_byte_data(file, addr);
1157         if (ret < 0)
1158         {
1159             std::cerr << "Error " << __FUNCTION__ << "\n";
1160             return false;
1161         }
1162         // ifdet is inverted
1163         val = static_cast<uint8_t>(~ret);
1164         return true;
1165     }
1166 
getFailedBackplane1167     bool getFailed(uint8_t& val)
1168     {
1169         constexpr uint8_t addr = 0xC;
1170         int ret = i2c_smbus_read_byte_data(file, addr);
1171         if (ret < 0)
1172         {
1173             std::cerr << "Error " << __FUNCTION__ << "\n";
1174             return false;
1175         }
1176         val = static_cast<uint8_t>(ret);
1177         return true;
1178     }
1179 
getRebuildBackplane1180     bool getRebuild(uint8_t& val)
1181     {
1182         constexpr uint8_t addr = 0xD;
1183         int ret = i2c_smbus_read_byte_data(file, addr);
1184         if (ret < 0)
1185         {
1186             std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
1187                       << "\n";
1188             return false;
1189         }
1190         val = static_cast<uint8_t>(ret);
1191         return true;
1192     }
1193 
getInsertedAndRemovedNvmeDrivesBackplane1194     bool getInsertedAndRemovedNvmeDrives(
1195         std::forward_list<std::string>& nvmeDrivesInserted,
1196         std::forward_list<std::string>& nvmeDrivesRemoved)
1197     {
1198         /* Get the current drives status */
1199         std::bitset<8> currDriveStatus;
1200         uint8_t nPresence;
1201         uint8_t nIfdet;
1202 
1203         if (!getPresence(nPresence) || !getIFDET(nIfdet))
1204         {
1205             /* Error getting value. Return */
1206             std::cerr << "Backplane " << name
1207                       << " failed to get drive status\n";
1208             return false;
1209         }
1210 
1211         std::string dbusHsbpName = boost::replace_all_copy(name, " ", "_");
1212         auto nvmeMap = hsbpConfig.hsbpNvmeMap.find(dbusHsbpName);
1213         if (nvmeMap == hsbpConfig.hsbpNvmeMap.end())
1214         {
1215             std::cerr << "Couldn't get the NVMe Map for the backplane : "
1216                       << name << "\n";
1217             return false;
1218         }
1219 
1220         /* NVMe drives do not assert PRSNTn, and as such do not get reported in
1221          * "presence" register, but assert ifdet low. This implies for a NVMe
1222          * drive to be present, corresponding precense bit has to be 0 and idfet
1223          * has to be 1 (as the values of these regosters are negated: check
1224          * getPresence() and getIfdet() functions) */
1225         for (uint8_t bit = 0; bit < 8; bit++)
1226         {
1227             if ((nPresence & (1U << bit)) == 0)
1228             {
1229                 if (nIfdet & (1U << bit))
1230                 {
1231                     currDriveStatus.set(bit);
1232                 }
1233             }
1234         }
1235 
1236         /* Determine Inserted and Removed Drives
1237          * Prev Bit | Curr Bit | Status
1238          *    0     |    0     | No Change
1239          *    0     |    1     | Inserted
1240          *    1     |    0     | Removed
1241          *    1     |    1     | No Change
1242          */
1243         for (uint8_t index = 0; index < 8; index++)
1244         {
1245             /* Inserted */
1246             if (!prevDriveStatus.test(index) && currDriveStatus.test(index))
1247             {
1248                 nvmeDrivesInserted.emplace_front(nvmeMap->second.at(index));
1249                 std::cerr << name << " : " << nvmeDrivesInserted.front()
1250                           << " Inserted !\n";
1251             }
1252 
1253             /* Removed */
1254             else if (prevDriveStatus.test(index) &&
1255                      !currDriveStatus.test(index))
1256             {
1257                 nvmeDrivesRemoved.emplace_front(nvmeMap->second.at(index));
1258                 std::cerr << name << " : " << nvmeDrivesRemoved.front()
1259                           << " Removed !\n";
1260             }
1261         }
1262 
1263         prevDriveStatus = currDriveStatus;
1264         return true;
1265     }
1266 
~BackplaneBackplane1267     virtual ~Backplane()
1268     {
1269         timer.cancel();
1270         objServer.remove_interface(hsbpItemIface);
1271         objServer.remove_interface(versionIface);
1272         objServer.remove_interface(storageInterface);
1273         objServer.remove_interface(assetInterface);
1274         objServer.remove_interface(activationIface);
1275         if (file >= 0)
1276         {
1277             close(file);
1278         }
1279     }
1280 
1281     size_t bus;
1282     size_t address;
1283     size_t backplaneIndex;
1284     std::string name;
1285     boost::asio::steady_timer timer;
1286     bool present = false;
1287     uint8_t typeId = 0;
1288     uint8_t bootVer = 0;
1289     uint8_t fpgaVer = 0;
1290     uint8_t securityRev = 0;
1291     uint8_t funSupported = 0;
1292     uint8_t presence = 0;
1293     uint8_t ifdet = 0;
1294     uint8_t failed = 0;
1295     uint8_t rebuilding = 0;
1296     std::bitset<8> prevDriveStatus;
1297 
1298     int file = -1;
1299 
1300     std::string type;
1301 
1302     std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
1303     std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
1304     std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
1305     std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
1306     std::shared_ptr<sdbusplus::asio::dbus_interface> activationIface;
1307     std::list<Drive> drives;
1308     std::vector<std::shared_ptr<Led>> leds;
1309     std::shared_ptr<boost::container::flat_set<Mux>> muxes;
1310 };
1311 
1312 /* Global HSBP backplanes and NVMe drives collection */
1313 std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
1314 std::list<Drive> ownerlessDrives; // drives without a backplane
1315 /***************************** End of Section *******************************/
1316 
1317 /****************************************************************************/
1318 /***************** Miscellaneous Class/Function Definitions *****************/
1319 /****************************************************************************/
1320 /* The purpose of this class is to sync the code flow. Often times there could
1321  * be multiple dbus calls which are async, and upon completely finishing all
1322  * Dbus calls, we need to call next function, or handle the error.
1323  * When an object of this class goes out of scope, the respective handlers
1324  * will be called */
1325 class AsyncCallbackHandler
1326 {
1327     bool errorOccurred = false;
1328     std::function<void()> onSuccess = nullptr;
1329     std::function<void()> onError = nullptr;
1330 
1331   public:
AsyncCallbackHandler(std::function<void ()> onSuccessIn,std::function<void ()> onErrorIn)1332     explicit AsyncCallbackHandler(std::function<void()> onSuccessIn,
1333                                   std::function<void()> onErrorIn) :
1334         onSuccess(std::move(onSuccessIn)), onError(std::move(onErrorIn))
1335     {}
1336 
setError()1337     void setError()
1338     {
1339         errorOccurred = true;
1340     }
1341 
~AsyncCallbackHandler()1342     ~AsyncCallbackHandler()
1343     {
1344         /* If error occurred flag was set, execute the error handler */
1345         if (errorOccurred)
1346         {
1347             /* Check if Error Handler is defined */
1348             if (onError)
1349             {
1350                 onError();
1351             }
1352 
1353             return;
1354         }
1355 
1356         /* If Success Handler is present, execute Success Handler */
1357         if (onSuccess)
1358         {
1359             onSuccess();
1360         }
1361     }
1362 };
1363 
stopHsbpManager()1364 void stopHsbpManager()
1365 {
1366     std::cerr << __FUNCTION__ << ": Stopping hsbp-manager\n";
1367     appState = AppState::idle;
1368     hsbpConfig.clearConfig();
1369     clockBuffers.clear();
1370     ioExpanders.clear();
1371     backplanes.clear();
1372 
1373     io.stop();
1374 }
1375 /***************************** End of Section *******************************/
1376 
1377 /****************************************************************************/
1378 /********* HSBP clock enable/disable related Function Definitions ***********/
1379 /****************************************************************************/
updateHsbpClocks(std::forward_list<std::string> & nvmeDrivesInserted,std::forward_list<std::string> & nvmeDrivesRemoved)1380 void updateHsbpClocks(std::forward_list<std::string>& nvmeDrivesInserted,
1381                       std::forward_list<std::string>& nvmeDrivesRemoved)
1382 {
1383     if (appState < AppState::backplanesLoaded)
1384     {
1385         std::cerr << "HSBP not initialized ! Cancelling Clock Update ! \n";
1386         return;
1387     }
1388 
1389     std::cerr << "Updating HSBP drive clocks ...\n";
1390 
1391     /* Loop through all clock buffers and try to update the clocks (this will be
1392      * done if the mode of operation of the clock buffer is SMBus) */
1393     for (auto& clockBuffer : clockBuffers)
1394     {
1395         if (!clockBuffer.enableDisableClock(nvmeDrivesInserted,
1396                                             nvmeDrivesRemoved))
1397         {
1398             std::cerr << "Error Occurred while setting the clock in \""
1399                       << clockBuffer.getName() << "\"\n";
1400         }
1401     }
1402 
1403     /* If there are drives yet to be updated, check all the IO Expanders in case
1404      * they are mapped to the drives and enable the respective IO */
1405     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1406     {
1407         for (auto& ioExpander : ioExpanders)
1408         {
1409             if (!ioExpander.enableDisableOuput(nvmeDrivesInserted,
1410                                                nvmeDrivesRemoved))
1411             {
1412                 std::cerr << "Error Occurred while setting the IO in \""
1413                           << ioExpander.getName() << "\"\n";
1414             }
1415         }
1416     }
1417 
1418     /* If there are drives still left, then one or more drives clock
1419      * enable/diable failed. There is a possibility of improper mapping or
1420      * current communication with the device failed */
1421     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1422     {
1423         std::cerr << "Critical Error !!!\nMapping issue detected !\n";
1424 
1425         if (!nvmeDrivesInserted.empty())
1426         {
1427             std::cerr << "The clock enable failed for : ";
1428             for (auto& nvme1 : nvmeDrivesInserted)
1429             {
1430                 std::cerr << nvme1 << ", ";
1431             }
1432             std::cerr << "\n";
1433         }
1434 
1435         if (!nvmeDrivesRemoved.empty())
1436         {
1437             std::cerr << "The clock disable failed for : ";
1438             for (auto& nvme1 : nvmeDrivesRemoved)
1439             {
1440                 std::cerr << nvme1 << ", ";
1441             }
1442             std::cerr << "\n";
1443         }
1444     }
1445 }
1446 
scanHsbpDrives(bool & hsbpDriveScanInProgress)1447 void scanHsbpDrives(bool& hsbpDriveScanInProgress)
1448 {
1449     std::cerr << __FUNCTION__ << ": Scanning HSBP drives status ...\n";
1450 
1451     /* List variables to store the drives Inserted/Removed */
1452     std::forward_list<std::string> nvmeDrivesInserted;
1453     std::forward_list<std::string> nvmeDrivesRemoved;
1454 
1455     /* Loop through each backplane present and get the list of inserted/removed
1456      * drives */
1457     for (auto& [name, backplane] : backplanes)
1458     {
1459         backplane->getInsertedAndRemovedNvmeDrives(nvmeDrivesInserted,
1460                                                    nvmeDrivesRemoved);
1461     }
1462 
1463     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1464     {
1465         updateHsbpClocks(nvmeDrivesInserted, nvmeDrivesRemoved);
1466     }
1467 
1468     std::cerr << __FUNCTION__ << ": Scanning HSBP drives Completed\n";
1469 
1470     hsbpDriveScanInProgress = false;
1471 }
1472 
checkHsbpDrivesStatus()1473 void checkHsbpDrivesStatus()
1474 {
1475     static bool hsbpDriveScanInProgress = false;
1476     static bool hsbpDriveRescanInQueue = false;
1477 
1478     if (appState < AppState::backplanesLoaded)
1479     {
1480         std::cerr << __FUNCTION__
1481                   << ": HSBP not initialized ! Cancelling scan of HSBP drives "
1482                      "status ! \n";
1483         return;
1484     }
1485 
1486     if (hsbpDriveScanInProgress)
1487     {
1488         /* Scan and Clock Update already in progress. Try again after sometime.
1489          * This event can occur due to the GPIO interrupt */
1490         std::cerr << __FUNCTION__
1491                   << ": HSBP Drives Scan is already in progress\n";
1492         if (hsbpDriveRescanInQueue)
1493         {
1494             /* There is already a Re-Scan in queue. No need to create multiple
1495              * rescans */
1496             return;
1497         }
1498 
1499         hsbpDriveRescanInQueue = true;
1500 
1501         std::cerr << __FUNCTION__ << ": Queuing the Scan \n";
1502 
1503         auto driveScanTimer = std::make_shared<boost::asio::steady_timer>(io);
1504         driveScanTimer->expires_after(std::chrono::seconds(1));
1505         driveScanTimer->async_wait(
1506             [driveScanTimer](const boost::system::error_code ec) {
1507                 if (ec == boost::asio::error::operation_aborted)
1508                 {
1509                     // Timer was Aborted
1510                     return;
1511                 }
1512                 else if (ec)
1513                 {
1514                     std::cerr << "driveScanTimer: Timer error" << ec.message()
1515                               << "\n";
1516                     return;
1517                 }
1518                 hsbpDriveRescanInQueue = false;
1519                 checkHsbpDrivesStatus();
1520             });
1521 
1522         return;
1523     }
1524 
1525     hsbpDriveScanInProgress = true;
1526 
1527     /* Post the scan to IO queue and return from here. This enables capturing
1528      * next GPIO event if any */
1529     boost::asio::post(io, []() { scanHsbpDrives(hsbpDriveScanInProgress); });
1530 }
1531 /***************************** End of Section *******************************/
1532 
1533 /****************************************************************************/
1534 /********** Backplanes and NVMe drives related Function Definitions *********/
1535 /****************************************************************************/
getDriveCount()1536 static size_t getDriveCount()
1537 {
1538     size_t count = 0;
1539     for (const auto& [key, backplane] : backplanes)
1540     {
1541         count += backplane->drives.size();
1542     }
1543     return count + ownerlessDrives.size();
1544 }
1545 
updateAssets()1546 void updateAssets()
1547 {
1548     appState = AppState::loadingDrives;
1549 
1550     /* Setup a callback to be called once the assets are populated completely or
1551      * fallback to error handler */
1552     auto drivesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1553         []() {
1554             appState = AppState::drivesLoaded;
1555             std::cerr << "Drives Updated !\n";
1556         },
1557         []() {
1558             // TODO: Handle this error if needed
1559             appState = AppState::backplanesLoaded;
1560             std::cerr << "An error occured ! Drives load failed \n";
1561         });
1562 
1563     conn->async_method_call(
1564         [drivesLoadedCallback](const boost::system::error_code ec,
1565                                const GetSubTreeType& subtree) {
1566             if (ec)
1567             {
1568                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1569                           << ec.message() << "\n";
1570                 drivesLoadedCallback->setError();
1571                 return;
1572             }
1573 
1574             // drives may get an owner during this, or we might disover more
1575             // drives
1576             ownerlessDrives.clear();
1577             for (const auto& [path, objDict] : subtree)
1578             {
1579                 if (objDict.empty())
1580                 {
1581                     continue;
1582                 }
1583 
1584                 const std::string& owner = objDict.begin()->first;
1585                 // we export this interface too
1586                 if (owner == busName)
1587                 {
1588                     continue;
1589                 }
1590                 if (std::find(objDict.begin()->second.begin(),
1591                               objDict.begin()->second.end(), assetTag) ==
1592                     objDict.begin()->second.end())
1593                 {
1594                     // no asset tag to associate to
1595                     continue;
1596                 }
1597 
1598                 conn->async_method_call(
1599                     [path, drivesLoadedCallback](
1600                         const boost::system::error_code ec2,
1601                         const boost::container::flat_map<
1602                             std::string, std::variant<uint64_t, std::string>>&
1603                             values) {
1604                         if (ec2)
1605                         {
1606                             std::cerr
1607                                 << __FUNCTION__ << ": Error Getting Config "
1608                                 << ec2.message() << " "
1609                                 << "\n";
1610                             drivesLoadedCallback->setError();
1611                             return;
1612                         }
1613                         auto findBus = values.find("Bus");
1614 
1615                         if (findBus == values.end())
1616                         {
1617                             std::cerr
1618                                 << __FUNCTION__ << ": Illegal interface at "
1619                                 << path << "\n";
1620                             drivesLoadedCallback->setError();
1621                             return;
1622                         }
1623 
1624                         // find the mux bus and addr
1625                         size_t muxBus = static_cast<size_t>(
1626                             std::get<uint64_t>(findBus->second));
1627                         std::filesystem::path muxPath =
1628                             "/sys/bus/i2c/devices/i2c-" +
1629                             std::to_string(muxBus) + "/mux_device";
1630                         if (!std::filesystem::is_symlink(muxPath))
1631                         {
1632                             std::cerr << path << " mux does not exist\n";
1633                             drivesLoadedCallback->setError();
1634                             return;
1635                         }
1636 
1637                         // we should be getting something of the form 7-0052
1638                         // for bus 7 addr 52
1639                         std::string fname =
1640                             std::filesystem::read_symlink(muxPath).filename();
1641                         auto findDash = fname.find('-');
1642 
1643                         if (findDash == std::string::npos ||
1644                             findDash + 1 >= fname.size())
1645                         {
1646                             std::cerr << path << " mux path invalid\n";
1647                             drivesLoadedCallback->setError();
1648                             return;
1649                         }
1650 
1651                         std::string busStr = fname.substr(0, findDash);
1652                         std::string muxStr = fname.substr(findDash + 1);
1653 
1654                         size_t bus = static_cast<size_t>(std::stoi(busStr));
1655                         size_t addr =
1656                             static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
1657                         size_t muxIndex = 0;
1658 
1659                         // find the channel of the mux the drive is on
1660                         std::ifstream nameFile(
1661                             "/sys/bus/i2c/devices/i2c-" +
1662                             std::to_string(muxBus) + "/name");
1663                         if (!nameFile)
1664                         {
1665                             std::cerr << __FUNCTION__
1666                                       << ": Unable to open name file of bus "
1667                                       << muxBus << "\n";
1668                             drivesLoadedCallback->setError();
1669                             return;
1670                         }
1671 
1672                         std::string nameStr;
1673                         std::getline(nameFile, nameStr);
1674 
1675                         // file is of the form "i2c-4-mux (chan_id 1)", get chan
1676                         // assume single digit chan
1677                         const std::string prefix = "chan_id ";
1678                         size_t findId = nameStr.find(prefix);
1679                         if (findId == std::string::npos ||
1680                             findId + 1 >= nameStr.size())
1681                         {
1682                             std::cerr
1683                                 << __FUNCTION__ << ": Illegal name file on bus "
1684                                 << muxBus << "\n";
1685                         }
1686 
1687                         std::string indexStr =
1688                             nameStr.substr(findId + prefix.size(), 1);
1689 
1690                         size_t driveIndex = std::stoi(indexStr);
1691 
1692                         boost::container::flat_map<std::string, std::string>
1693                             assetInventory;
1694                         const std::array<const char*, 4> assetKeys = {
1695                             "PartNumber", "SerialNumber", "Manufacturer",
1696                             "Model"};
1697                         for (const auto& [key, value] : values)
1698                         {
1699                             if (std::find(assetKeys.begin(), assetKeys.end(),
1700                                           key) == assetKeys.end())
1701                             {
1702                                 continue;
1703                             }
1704                             if (std::holds_alternative<std::string>(value))
1705                             {
1706                                 assetInventory[key] =
1707                                     std::get<std::string>(value);
1708                             }
1709                         }
1710 
1711                         Backplane* parent = nullptr;
1712                         for (auto& [name, backplane] : backplanes)
1713                         {
1714                             muxIndex = 0;
1715                             for (const Mux& mux : *(backplane->muxes))
1716                             {
1717                                 if (bus == mux.bus && addr == mux.address)
1718                                 {
1719                                     parent = backplane.get();
1720                                     break;
1721                                 }
1722                                 muxIndex += mux.channels;
1723                             }
1724                             if (parent)
1725                             {
1726                                 /* Found the backplane. No need to proceed
1727                                  * further */
1728                                 break;
1729                             }
1730                         }
1731 
1732                         // assume its a M.2 or something without a hsbp
1733                         if (parent == nullptr)
1734                         {
1735                             std::string driveName =
1736                                 "Drive_" + std::to_string(getDriveCount() + 1);
1737                             auto& drive = ownerlessDrives.emplace_back(
1738                                 driveName, true, true, true, false);
1739                             drive.createAsset(assetInventory);
1740                             return;
1741                         }
1742 
1743                         driveIndex += muxIndex;
1744 
1745                         if (parent->drives.size() <= driveIndex)
1746                         {
1747                             std::cerr
1748                                 << __FUNCTION__ << ": Illegal drive index at "
1749                                 << path << " " << driveIndex << "\n";
1750                             drivesLoadedCallback->setError();
1751                             return;
1752                         }
1753                         auto it = parent->drives.begin();
1754                         std::advance(it, driveIndex);
1755 
1756                         it->createAsset(assetInventory);
1757                     },
1758                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1759                     "" /*all interface items*/);
1760             }
1761         },
1762         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1763         0, std::array<const char*, 1>{nvmeIntf});
1764 }
1765 
populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,std::string & rootPath)1766 void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
1767                    std::string& rootPath)
1768 {
1769     const static std::array<const std::string, 4> muxTypes = {
1770         "xyz.openbmc_project.Configuration.PCA9543Mux",
1771         "xyz.openbmc_project.Configuration.PCA9544Mux",
1772         "xyz.openbmc_project.Configuration.PCA9545Mux",
1773         "xyz.openbmc_project.Configuration.PCA9546Mux"};
1774 
1775     conn->async_method_call(
1776         [muxes](const boost::system::error_code ec,
1777                 const GetSubTreeType& subtree) {
1778             if (ec)
1779             {
1780                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1781                           << ec.message() << "\n";
1782                 return;
1783             }
1784             size_t index = 0; // as we use a flat map, these are sorted
1785             for (const auto& [path, objDict] : subtree)
1786             {
1787                 if (objDict.empty() || objDict.begin()->second.empty())
1788                 {
1789                     continue;
1790                 }
1791 
1792                 const std::string& owner = objDict.begin()->first;
1793                 const std::vector<std::string>& interfaces =
1794                     objDict.begin()->second;
1795 
1796                 const std::string* interface = nullptr;
1797                 for (const std::string& iface : interfaces)
1798                 {
1799                     if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
1800                         muxTypes.end())
1801                     {
1802                         interface = &iface;
1803                         break;
1804                     }
1805                 }
1806 
1807                 if (interface == nullptr)
1808                 {
1809                     std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
1810                     continue;
1811                 }
1812 
1813                 conn->async_method_call(
1814                     [path, muxes, index](
1815                         const boost::system::error_code ec2,
1816                         const boost::container::flat_map<
1817                             std::string,
1818                             std::variant<uint64_t, std::vector<std::string>>>&
1819                             values) {
1820                         if (ec2)
1821                         {
1822                             std::cerr
1823                                 << __FUNCTION__ << ": Error Getting Config "
1824                                 << ec2.message() << "\n";
1825                             return;
1826                         }
1827                         auto findBus = values.find("Bus");
1828                         auto findAddress = values.find("Address");
1829                         auto findChannelNames = values.find("ChannelNames");
1830                         if (findBus == values.end() ||
1831                             findAddress == values.end())
1832                         {
1833                             std::cerr
1834                                 << __FUNCTION__ << ": Illegal configuration at "
1835                                 << path << "\n";
1836                             return;
1837                         }
1838                         size_t bus = static_cast<size_t>(
1839                             std::get<uint64_t>(findBus->second));
1840                         size_t address = static_cast<size_t>(
1841                             std::get<uint64_t>(findAddress->second));
1842                         std::vector<std::string> channels =
1843                             std::get<std::vector<std::string>>(
1844                                 findChannelNames->second);
1845                         muxes->emplace(bus, address, channels.size(), index);
1846                     },
1847                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1848                     *interface);
1849                 index++;
1850             }
1851         },
1852         mapper::busName, mapper::path, mapper::interface, mapper::subtree,
1853         rootPath, 1, muxTypes);
1854 }
1855 
populateHsbpBackplanes(const std::shared_ptr<AsyncCallbackHandler> & backplanesLoadedCallback)1856 void populateHsbpBackplanes(
1857     const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
1858 {
1859     std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
1860     appState = AppState::loadingBackplanes;
1861     backplanes.clear();
1862 
1863     conn->async_method_call(
1864         [backplanesLoadedCallback](const boost::system::error_code ec,
1865                                    const GetSubTreeType& subtree) {
1866             if (ec)
1867             {
1868                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1869                           << ec.message() << "\n";
1870                 backplanesLoadedCallback->setError();
1871                 return;
1872             }
1873 
1874             if (subtree.empty())
1875             {
1876                 /* There wer no HSBP's detected. set teh state back to
1877                  * componentsLoaded so that on backplane match event, the
1878                  * process can start again */
1879                 appState = AppState::componentsLoaded;
1880                 std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
1881                 return;
1882             }
1883 
1884             for (const auto& [path, objDict] : subtree)
1885             {
1886                 if (objDict.empty())
1887                 {
1888                     std::cerr << __FUNCTION__
1889                               << ": Subtree data "
1890                                  "corrupted !\n";
1891                     backplanesLoadedCallback->setError();
1892                     return;
1893                 }
1894 
1895                 const std::string& owner = objDict.begin()->first;
1896                 conn->async_method_call(
1897                     [backplanesLoadedCallback, path,
1898                      owner](const boost::system::error_code ec2,
1899                             const boost::container::flat_map<
1900                                 std::string, BasicVariantType>& resp) {
1901                         if (ec2)
1902                         {
1903                             std::cerr
1904                                 << __FUNCTION__ << ": Error Getting Config "
1905                                 << ec2.message() << "\n";
1906                             backplanesLoadedCallback->setError();
1907                             return;
1908                         }
1909                         std::optional<size_t> bus;
1910                         std::optional<size_t> address;
1911                         std::optional<size_t> backplaneIndex;
1912                         std::optional<std::string> name;
1913                         for (const auto& [key, value] : resp)
1914                         {
1915                             if (key == "Bus")
1916                             {
1917                                 bus = std::get<uint64_t>(value);
1918                             }
1919                             else if (key == "Address")
1920                             {
1921                                 address = std::get<uint64_t>(value);
1922                             }
1923                             else if (key == "Index")
1924                             {
1925                                 backplaneIndex = std::get<uint64_t>(value);
1926                             }
1927                             else if (key == "Name")
1928                             {
1929                                 name = std::get<std::string>(value);
1930                             }
1931                         }
1932                         if (!bus || !address || !name || !backplaneIndex)
1933                         {
1934                             std::cerr
1935                                 << __FUNCTION__ << ": Illegal configuration at "
1936                                 << path << "\n";
1937                             backplanesLoadedCallback->setError();
1938                             return;
1939                         }
1940                         std::string parentPath =
1941                             std::filesystem::path(path).parent_path();
1942                         const auto& [backplane, status] = backplanes.emplace(
1943                             *name, std::make_shared<Backplane>(
1944                                        *bus, *address, *backplaneIndex, *name));
1945                         backplane->second->run(parentPath, owner);
1946                         populateMuxes(backplane->second->muxes, parentPath);
1947                     },
1948                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1949                     hsbpCpldInft);
1950             }
1951         },
1952         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1953         0, std::array<const char*, 1>{hsbpCpldInft});
1954 }
1955 
setUpBackplanesAndDrives()1956 void setUpBackplanesAndDrives()
1957 {
1958     static bool backplanesScanInProgress = false;
1959     static bool backplanesRescanInQueue = false;
1960 
1961     if (appState < AppState::componentsLoaded)
1962     {
1963         std::cerr << __FUNCTION__
1964                   << ": Components are not initialized ! Cancelling scan of "
1965                      "Backplanes ! \n";
1966         return;
1967     }
1968 
1969     if (backplanesScanInProgress)
1970     {
1971         std::cerr << __FUNCTION__
1972                   << ": Backplanes Scan is already in progress\n";
1973         if (backplanesRescanInQueue)
1974         {
1975             /* There is already a Re-Scan in queue. No need to create multiple
1976              * rescans */
1977             return;
1978         }
1979 
1980         backplanesRescanInQueue = true;
1981 
1982         std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
1983 
1984         auto backplaneScanTimer =
1985             std::make_shared<boost::asio::steady_timer>(io);
1986         backplaneScanTimer->expires_after(std::chrono::seconds(1));
1987         backplaneScanTimer->async_wait(
1988             [backplaneScanTimer](const boost::system::error_code ec) {
1989                 if (ec == boost::asio::error::operation_aborted)
1990                 {
1991                     // Timer was Aborted
1992                     return;
1993                 }
1994                 else if (ec)
1995                 {
1996                     std::cerr << "backplaneScanTimer: Timer error"
1997                               << ec.message() << "\n";
1998                     return;
1999                 }
2000                 backplanesRescanInQueue = false;
2001                 setUpBackplanesAndDrives();
2002             });
2003 
2004         return;
2005     }
2006 
2007     backplanesScanInProgress = true;
2008 
2009     /* Set Callback to be called once backplanes are populated to call
2010      * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
2011     auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
2012         []() {
2013             /* If no HSBP's were detected, the state changes to
2014              * componentsLoaded. Proceed further only if state was
2015              * loadingBackplanes */
2016             if (appState != AppState::loadingBackplanes)
2017             {
2018                 backplanesScanInProgress = false;
2019                 return;
2020             }
2021 
2022             /* If there is a ReScan in the Queue, dont proceed further. Load the
2023              * Backplanes again and then proceed further */
2024             if (backplanesRescanInQueue)
2025             {
2026                 backplanesScanInProgress = false;
2027                 return;
2028             }
2029 
2030             appState = AppState::backplanesLoaded;
2031             std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
2032 
2033             checkHsbpDrivesStatus();
2034             updateAssets();
2035             backplanesScanInProgress = false;
2036         },
2037         []() {
2038             /* Loading Backplanes is an important step. If the load failed due
2039              * to an error, stop the app so that restart cant be triggerred */
2040             std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
2041             appState = AppState::idle;
2042             backplanesScanInProgress = false;
2043             stopHsbpManager();
2044         });
2045 
2046     populateHsbpBackplanes(backplanesLoadedCallback);
2047 }
2048 
setupBackplanesAndDrivesMatch()2049 void setupBackplanesAndDrivesMatch()
2050 {
2051     static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
2052         *conn,
2053         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2054         "member='PropertiesChanged', "
2055         "interface='org.freedesktop.DBus.Properties', "
2056         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2057             std::string(hsbpCpldInft) + "'",
2058         [](sdbusplus::message_t& msg) {
2059             std::string intfName;
2060             boost::container::flat_map<std::string, BasicVariantType> values;
2061             msg.read(intfName, values);
2062 
2063             /* This match will be triggered for each of the property being set
2064              * under the hsbpCpldInft interface. Call the loader only on one
2065              * property say "name". This will avoid multiple calls to populate
2066              * function
2067              */
2068             for (const auto& [key, value] : values)
2069             {
2070                 if (key == "Name")
2071                 {
2072                     /* This match will be triggered when ever there is a
2073                      * addition/removal of HSBP backplane. At this stage, all
2074                      * the HSBP's need to be populated again and also assets
2075                      * have to be re-discovered. So, setting state to
2076                      * componentsLoaded and calling setUpBackplanesAndDrives()
2077                      * only if configuration and components loading was
2078                      * completed */
2079                     if (appState < AppState::componentsLoaded)
2080                     {
2081                         /* Configuration is not loaded yet. Backplanes will be
2082                          * loaded
2083                          * once configuration and components are loaded. */
2084                         std::cerr
2085                             << __FUNCTION__ << ": Discarding Backplane match\n";
2086                         return;
2087                     }
2088 
2089                     appState = AppState::componentsLoaded;
2090 
2091                     /* We will call the function after a small delay to let all
2092                      * the properties to be intialized */
2093                     auto backplaneTimer =
2094                         std::make_shared<boost::asio::steady_timer>(io);
2095                     backplaneTimer->expires_after(std::chrono::seconds(2));
2096                     backplaneTimer->async_wait(
2097                         [backplaneTimer](const boost::system::error_code ec) {
2098                             if (ec == boost::asio::error::operation_aborted)
2099                             {
2100                                 return;
2101                             }
2102                             else if (ec)
2103                             {
2104                                 std::cerr << "backplaneTimer: Timer error"
2105                                           << ec.message() << "\n";
2106                                 return;
2107                             }
2108                             setUpBackplanesAndDrives();
2109                         });
2110                 }
2111             }
2112         });
2113 
2114     static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
2115         *conn,
2116         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2117         "member='PropertiesChanged', "
2118         "interface='org.freedesktop.DBus.Properties', arg0='" +
2119             std::string(nvmeIntf) + "'",
2120         [](sdbusplus::message_t& msg) {
2121             std::string intfName;
2122             boost::container::flat_map<std::string, BasicVariantType> values;
2123             msg.read(intfName, values);
2124 
2125             /* This match will be triggered for each of the property being set
2126              * under the nvmeIntf interface. Call the loader only on one
2127              * property say "name". This will avoid multiple calls to populate
2128              * function
2129              */
2130             for (const auto& [key, value] : values)
2131             {
2132                 if (key == "Name")
2133                 {
2134                     /* This match will be triggered when ever there is a
2135                      * addition/removal of drives. At this stage only assets
2136                      * have to be re-discovered. So, setting state to
2137                      * backplanesLoaded and calling updateAssets() only if all
2138                      * previous states are completed */
2139                     if (appState < AppState::backplanesLoaded)
2140                     {
2141                         /* Configuration is not loaded yet. Drives will be
2142                          * loaded once
2143                          * configuration, components and backplanes are loaded.
2144                          */
2145                         std::cerr
2146                             << __FUNCTION__ << ": Discarding Drive match\n";
2147                         return;
2148                     }
2149 
2150                     appState = AppState::backplanesLoaded;
2151 
2152                     /* We will call the function after a small delay to let all
2153                      * the properties to be intialized */
2154                     auto driveTimer =
2155                         std::make_shared<boost::asio::steady_timer>(io);
2156                     driveTimer->expires_after(std::chrono::seconds(2));
2157                     driveTimer->async_wait(
2158                         [driveTimer](const boost::system::error_code ec) {
2159                             if (ec == boost::asio::error::operation_aborted)
2160                             {
2161                                 return;
2162                             }
2163                             else if (ec)
2164                             {
2165                                 std::cerr << "driveTimer: Timer error"
2166                                           << ec.message() << "\n";
2167                                 return;
2168                             }
2169                             updateAssets();
2170                         });
2171                 }
2172             }
2173         });
2174 }
2175 /***************************** End of Section *******************************/
2176 
2177 /****************************************************************************/
2178 /******************* Components related Function Definitions ****************/
2179 /****************************************************************************/
verifyComponentsLoaded()2180 bool verifyComponentsLoaded()
2181 {
2182     std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
2183 
2184     /* Loop through all clock buffers */
2185     for (auto& clockBuffer : clockBuffers)
2186     {
2187         if (!clockBuffer.isInitialized())
2188         {
2189             std::cerr << "Critical Error: Initializing \""
2190                       << clockBuffer.getName() << "\" failed\n";
2191             return false;
2192         }
2193     }
2194 
2195     /* Loop through all IO Expanders */
2196     for (auto& ioExpander : ioExpanders)
2197     {
2198         if (!ioExpander.isInitialized())
2199         {
2200             std::cerr << "Critical Error: Initializing \""
2201                       << ioExpander.getName() << "\" failed\n";
2202             return false;
2203         }
2204     }
2205 
2206     std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
2207 
2208     return true;
2209 }
2210 /***************************** End of Section *******************************/
2211 
2212 /****************************************************************************/
2213 /****************** IO expander related Function Definitions ****************/
2214 /****************************************************************************/
loadIoExpanderInfo(const std::shared_ptr<AsyncCallbackHandler> & componentsLoadedCallback)2215 void loadIoExpanderInfo(
2216     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2217 {
2218     appState = AppState::loadingComponents;
2219 
2220     /* Clear global ioExpanders to start off */
2221     ioExpanders.clear();
2222 
2223     conn->async_method_call(
2224         [componentsLoadedCallback](const boost::system::error_code ec,
2225                                    const GetSubTreeType& subtree) {
2226             if (ec)
2227             {
2228                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2229                           << ec.message() << "\n";
2230                 componentsLoadedCallback->setError();
2231                 return;
2232             }
2233 
2234             for (auto& [path, objDict] : subtree)
2235             {
2236                 if (objDict.empty())
2237                 {
2238                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2239                     componentsLoadedCallback->setError();
2240                     return;
2241                 }
2242 
2243                 /* Ideally there would be only one element in objDict as only
2244                  * one service exposes it and there would be only one interface
2245                  * so it is safe to directly read them without loop */
2246                 const std::string& service = objDict.begin()->first;
2247                 const std::string& intf = objDict.begin()->second.front();
2248 
2249                 conn->async_method_call(
2250                     [componentsLoadedCallback](
2251                         const boost::system::error_code er,
2252                         const boost::container::flat_map<
2253                             std::string, BasicVariantType>& resp) {
2254                         if (er)
2255                         {
2256                             std::cerr << __FUNCTION__
2257                                       << ": Error Getting "
2258                                          "Config "
2259                                       << er.message() << "\n";
2260                             componentsLoadedCallback->setError();
2261                             return;
2262                         }
2263 
2264                         std::optional<uint64_t> bus;
2265                         std::optional<uint64_t> address;
2266                         std::optional<uint64_t> confIORegAddr;
2267                         std::optional<uint64_t> outCtrlBaseAddr;
2268                         std::optional<uint64_t> outCtrlByteCount;
2269                         std::unordered_map<std::string,
2270                                            std::vector<std::string>>
2271                             ioMap;
2272                         std::optional<std::string> name;
2273                         std::optional<std::string> type;
2274 
2275                         /* Loop through to get all IO Expander properties */
2276                         for (const auto& [key, value] : resp)
2277                         {
2278                             if (key == "Bus")
2279                             {
2280                                 bus = std::get<uint64_t>(value);
2281                             }
2282                             else if (key == "Address")
2283                             {
2284                                 address = std::get<uint64_t>(value);
2285                             }
2286                             else if (key == "ConfIORegAddr")
2287                             {
2288                                 confIORegAddr = std::get<uint64_t>(value);
2289                             }
2290                             else if (key == "OutCtrlBaseAddr")
2291                             {
2292                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2293                             }
2294                             else if (key == "OutCtrlByteCount")
2295                             {
2296                                 outCtrlByteCount = std::get<uint64_t>(value);
2297                             }
2298                             else if (key == "Name")
2299                             {
2300                                 name = std::get<std::string>(value);
2301                             }
2302                             else if (key == "Type")
2303                             {
2304                                 type = std::get<std::string>(value);
2305                             }
2306                             else if (key.starts_with("IO"))
2307                             {
2308                                 std::optional<std::vector<std::string>> outList;
2309                                 outList = std::get<NvmeMapping>(value);
2310                                 if (!outList)
2311                                 {
2312                                     break;
2313                                 }
2314                                 ioMap.try_emplace(key, *outList);
2315                             }
2316                         }
2317 
2318                         /* Verify if all properties were defined */
2319                         if (!bus || !address || !confIORegAddr ||
2320                             !outCtrlBaseAddr || !outCtrlByteCount || !name)
2321                         {
2322                             std::cerr << __FUNCTION__
2323                                       << ": Incomplete "
2324                                          "Clock Buffer definition !! \n";
2325                             componentsLoadedCallback->setError();
2326                             return;
2327                         }
2328 
2329                         /* Check if we were able to get byteMap correctly */
2330                         if ((*outCtrlByteCount) != ioMap.size())
2331                         {
2332                             std::cerr << "loadIoExpanderInfo(): Incomplete "
2333                                          "IO Map !! \n";
2334                             componentsLoadedCallback->setError();
2335                             return;
2336                         }
2337 
2338                         /* Create IO expander object and add it to global
2339                          * ioExpanders vector */
2340                         ioExpanders.emplace_front(
2341                             *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
2342                             *outCtrlByteCount, ioMap, *name, *type);
2343                     },
2344                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2345                     intf);
2346             }
2347         },
2348         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2349         0, hsbpConfig.ioExpanderTypes);
2350 }
2351 /***************************** End of Section *******************************/
2352 
2353 /****************************************************************************/
2354 /***************** Clock buffer related Function Definitions ****************/
2355 /****************************************************************************/
loadClockBufferInfo(const std::shared_ptr<AsyncCallbackHandler> & componentsLoadedCallback)2356 void loadClockBufferInfo(
2357     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2358 {
2359     appState = AppState::loadingComponents;
2360 
2361     /* Clear global clockBuffers to start off */
2362     clockBuffers.clear();
2363 
2364     conn->async_method_call(
2365         [componentsLoadedCallback](const boost::system::error_code ec,
2366                                    const GetSubTreeType& subtree) {
2367             if (ec)
2368             {
2369                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2370                           << ec.message() << "\n";
2371                 componentsLoadedCallback->setError();
2372                 return;
2373             }
2374 
2375             for (auto& [path, objDict] : subtree)
2376             {
2377                 if (objDict.empty())
2378                 {
2379                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2380                     componentsLoadedCallback->setError();
2381                     return;
2382                 }
2383 
2384                 /* Ideally there would be only one element in objDict as only
2385                  * one service exposes it and there would be only one interface
2386                  * so it is safe to directly read them without loop */
2387                 const std::string& service = objDict.begin()->first;
2388                 const std::string& intf = objDict.begin()->second.front();
2389 
2390                 conn->async_method_call(
2391                     [componentsLoadedCallback](
2392                         const boost::system::error_code er,
2393                         const boost::container::flat_map<
2394                             std::string, BasicVariantType>& resp) {
2395                         if (er)
2396                         {
2397                             std::cerr << __FUNCTION__
2398                                       << ": Error Getting "
2399                                          "Config "
2400                                       << er.message() << "\n";
2401                             componentsLoadedCallback->setError();
2402                             return;
2403                         }
2404 
2405                         std::optional<uint64_t> bus;
2406                         std::optional<uint64_t> address;
2407                         std::optional<std::string> mode;
2408                         std::optional<uint64_t> outCtrlBaseAddr;
2409                         std::optional<uint64_t> outCtrlByteCount;
2410                         std::unordered_map<std::string,
2411                                            std::vector<std::string>>
2412                             byteMap;
2413                         std::optional<std::string> name;
2414                         std::optional<std::string> type;
2415 
2416                         /* Loop through to get all Clock Buffer properties */
2417                         for (const auto& [key, value] : resp)
2418                         {
2419                             if (key == "Bus")
2420                             {
2421                                 bus = std::get<uint64_t>(value);
2422                             }
2423                             else if (key == "Address")
2424                             {
2425                                 address = std::get<uint64_t>(value);
2426                             }
2427                             else if (key == "Mode")
2428                             {
2429                                 mode = std::get<std::string>(value);
2430                             }
2431                             else if (key == "OutCtrlBaseAddr")
2432                             {
2433                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2434                             }
2435                             else if (key == "OutCtrlByteCount")
2436                             {
2437                                 outCtrlByteCount = std::get<uint64_t>(value);
2438                             }
2439                             else if (key == "Name")
2440                             {
2441                                 name = std::get<std::string>(value);
2442                             }
2443                             else if (key == "Type")
2444                             {
2445                                 type = std::get<std::string>(value);
2446                             }
2447                             else if (key.starts_with("Byte"))
2448                             {
2449                                 std::optional<std::vector<std::string>>
2450                                     byteList;
2451                                 byteList = std::get<NvmeMapping>(value);
2452                                 if (!byteList)
2453                                 {
2454                                     break;
2455                                 }
2456                                 byteMap.try_emplace(key, *byteList);
2457                             }
2458                         }
2459 
2460                         /* Verify if all properties were defined */
2461                         if (!bus || !address || !mode || !outCtrlBaseAddr ||
2462                             !outCtrlByteCount || !name)
2463                         {
2464                             std::cerr << __FUNCTION__
2465                                       << ": Incomplete "
2466                                          "Clock Buffer definition !! \n";
2467                             componentsLoadedCallback->setError();
2468                             return;
2469                         }
2470 
2471                         /* Check if we were able to get byteMap correctly */
2472                         if ((*outCtrlByteCount) != byteMap.size())
2473                         {
2474                             std::cerr << __FUNCTION__
2475                                       << ": Incomplete "
2476                                          "Byte Map !! \n";
2477                             componentsLoadedCallback->setError();
2478                             return;
2479                         }
2480 
2481                         /* Create clock buffer object and add it to global
2482                          * clockBuffers vector */
2483                         clockBuffers.emplace_front(
2484                             *bus, *address, *mode, *outCtrlBaseAddr,
2485                             *outCtrlByteCount, byteMap, *name, *type);
2486                     },
2487                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2488                     intf);
2489             }
2490         },
2491         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2492         0, hsbpConfig.clockBufferTypes);
2493 }
2494 /***************************** End of Section *******************************/
2495 
2496 /****************************************************************************/
2497 /***************** HSBP Config related Function Definitions *****************/
2498 /****************************************************************************/
loadHsbpConfig()2499 void loadHsbpConfig()
2500 {
2501     appState = AppState::loadingHsbpConfig;
2502 
2503     conn->async_method_call(
2504         [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
2505             if (ec)
2506             {
2507                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2508                           << ec.message() << "\n";
2509                 return;
2510             }
2511 
2512             if (subtree.empty())
2513             {
2514                 /* Entity manager is either still loading the configuration or
2515                  * failed to load. In either way, return from here as the dbus
2516                  * match will take care */
2517                 std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
2518                 return;
2519             }
2520 
2521             /* There should be only one HSBP Configureation exposed */
2522             if (subtree.size() != 1)
2523             {
2524                 std::cerr << __FUNCTION__
2525                           << ": Multiple configurations "
2526                              "detected !!\n";
2527                 /* Critical Error. Stop Application */
2528                 stopHsbpManager();
2529                 return;
2530             }
2531 
2532             auto& path = subtree.begin()->first;
2533             auto& objDict = subtree.begin()->second;
2534 
2535             if (objDict.empty())
2536             {
2537                 /* Critical Error. Stop Application */
2538                 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2539                 stopHsbpManager();
2540                 return;
2541             }
2542 
2543             const std::string& service = objDict.begin()->first;
2544 
2545             conn->async_method_call(
2546                 [](const boost::system::error_code er,
2547                    const boost::container::flat_map<std::string,
2548                                                     BasicVariantType>& resp) {
2549                     if (er)
2550                     {
2551                         std::cerr << __FUNCTION__ << ": Error Getting Config "
2552                                   << er.message() << "\n";
2553                         /* Critical Error. Stop Application */
2554                         stopHsbpManager();
2555                         return;
2556                     }
2557 
2558                     std::optional<uint64_t> rootI2cBus;
2559                     std::optional<std::vector<std::string>> supportedHsbps;
2560                     std::optional<std::vector<std::string>> clockBufferTypes;
2561                     std::optional<std::vector<std::string>> ioExpanderTypes;
2562 
2563                     /* Loop through to get root i2c bus and list of supported
2564                      * HSBPs */
2565                     for (const auto& [key, value] : resp)
2566                     {
2567                         if (key == "HsbpSupported")
2568                         {
2569                             supportedHsbps =
2570                                 std::get<std::vector<std::string>>(value);
2571                         }
2572                         else if (key == "RootI2cBus")
2573                         {
2574                             rootI2cBus = std::get<uint64_t>(value);
2575                         }
2576                         else if (key == "ClockBuffer")
2577                         {
2578                             clockBufferTypes =
2579                                 std::get<std::vector<std::string>>(value);
2580                         }
2581                         else if (key == "IoExpander")
2582                         {
2583                             ioExpanderTypes =
2584                                 std::get<std::vector<std::string>>(value);
2585                         }
2586                     }
2587 
2588                     /* Verify if i2c bus, supported HSBP's and clock buffers
2589                      * were defined (IO Expanders are optional) */
2590                     if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
2591                     {
2592                         std::cerr << __FUNCTION__
2593                                   << ": Incomplete HSBP "
2594                                      "configuration !! \n";
2595                         /* Critical Error. Stop Application */
2596                         stopHsbpManager();
2597                         return;
2598                     }
2599 
2600                     /* Clear and Load all details to global hsbp configuration
2601                      * variable */
2602                     hsbpConfig.clearConfig();
2603                     hsbpConfig.rootBus = *rootI2cBus;
2604                     hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
2605 
2606                     for (auto& clkBuffType : *clockBufferTypes)
2607                     {
2608                         hsbpConfig.clockBufferTypes.emplace_back(
2609                             "xyz.openbmc_project.Configuration." + clkBuffType);
2610                     }
2611 
2612                     if (ioExpanderTypes)
2613                     {
2614                         for (auto& ioCntrType : *ioExpanderTypes)
2615                         {
2616                             hsbpConfig.ioExpanderTypes.emplace_back(
2617                                 "xyz.openbmc_project.Configuration." +
2618                                 ioCntrType);
2619                         }
2620                     }
2621 
2622                     /* Loop through to get HSBP-NVME map and Components map
2623                      * details */
2624                     uint8_t hsbpMapCount = 0;
2625                     for (const auto& [key, value] : resp)
2626                     {
2627                         if (std::find(hsbpConfig.supportedHsbps.begin(),
2628                                       hsbpConfig.supportedHsbps.end(), key) !=
2629                             hsbpConfig.supportedHsbps.end())
2630                         {
2631                             std::optional<std::vector<std::string>> hsbpMap;
2632                             hsbpMap = std::get<NvmeMapping>(value);
2633                             if (!hsbpMap)
2634                             {
2635                                 break;
2636                             }
2637                             hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
2638                             hsbpMapCount++;
2639                         }
2640                     }
2641 
2642                     /* Check if we were able to get all the HSBP-NVMe maps */
2643                     if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
2644                     {
2645                         std::cerr << __FUNCTION__
2646                                   << ": Incomplete HSBP Map "
2647                                      "details !! \n";
2648                         /* Critical Error. Stop Application */
2649                         stopHsbpManager();
2650                         return;
2651                     }
2652 
2653                     /* HSBP configuration is loaded */
2654                     appState = AppState::hsbpConfigLoaded;
2655                     std::cerr << "HSBP Config loaded !\n";
2656 
2657                     /* Get Clock buffers and IO expander details. Create shared
2658                      * object of AsyncCallbackHandler with success and error
2659                      * callback */
2660                     auto componentsLoadedCallback = std::make_shared<
2661                         AsyncCallbackHandler>(
2662                         []() {
2663                             /* Verify if all components were initialized without
2664                              * errors */
2665                             if (!verifyComponentsLoaded())
2666                             {
2667                                 /* The application cannot proceed further as
2668                                  * components initialization failed. App needs
2669                                  * Restart */
2670                                 appState = AppState::idle;
2671                                 std::cerr
2672                                     << "One or more Componenets initialization "
2673                                        "failed !! Restart Required !\n";
2674                                 stopHsbpManager();
2675                             }
2676 
2677                             appState = AppState::componentsLoaded;
2678                             setUpBackplanesAndDrives();
2679                         },
2680                         []() {
2681                             /* The application cannot proceed further as
2682                              * components load failed. App needs Restart */
2683                             appState = AppState::idle;
2684                             std::cerr << "Loading Componenets failed !! "
2685                                          "Restart Required !\n";
2686                             stopHsbpManager();
2687                         });
2688 
2689                     loadClockBufferInfo(componentsLoadedCallback);
2690 
2691                     if (ioExpanderTypes)
2692                     {
2693                         loadIoExpanderInfo(componentsLoadedCallback);
2694                     }
2695                 },
2696                 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2697                 hsbpConfigIntf);
2698         },
2699         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2700         0, std::array<const char*, 1>{hsbpConfigIntf});
2701 }
2702 
setupHsbpConfigMatch()2703 void setupHsbpConfigMatch()
2704 {
2705     static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
2706         *conn,
2707         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2708         "member='PropertiesChanged', "
2709         "interface='org.freedesktop.DBus.Properties', "
2710         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2711             std::string(hsbpConfigIntf) + "'",
2712         [](sdbusplus::message_t& msg) {
2713             std::string intfName;
2714             boost::container::flat_map<std::string, BasicVariantType> values;
2715             msg.read(intfName, values);
2716 
2717             /* This match will be triggered for each of the property being set
2718              * under the hsbpConfig interface. "HsbpSupported" is one of the
2719              * important property which will enable us to read other properties.
2720              * So, when the match event occurs for "HsbpSupported" property
2721              * being set, we will call "loadHsbpConfig()" If the control has
2722              * come here, its either the first initialization or entity-manager
2723              * reload. So, we will reset the state to uninitialized
2724              */
2725             for (const auto& [key, value] : values)
2726             {
2727                 if (key == "HsbpSupported")
2728                 {
2729                     /* Configuration change detected, change the state to stop
2730                      * other processing */
2731                     appState = AppState::idle;
2732 
2733                     /* We will call the function after a small delay to let all
2734                      * the properties to be intialized */
2735                     auto loadTimer =
2736                         std::make_shared<boost::asio::steady_timer>(io);
2737                     loadTimer->expires_after(std::chrono::seconds(1));
2738                     loadTimer->async_wait(
2739                         [loadTimer](const boost::system::error_code ec) {
2740                             if (ec == boost::asio::error::operation_aborted)
2741                             {
2742                                 return;
2743                             }
2744                             else if (ec)
2745                             {
2746                                 std::cerr << __FUNCTION__ << ": Timer error"
2747                                           << ec.message() << "\n";
2748                                 if (hsbpConfig.supportedHsbps.empty())
2749                                 {
2750                                     /* Critical Error as none of the
2751                                      * configuration was loaded and timer
2752                                      * failed. Stop the application */
2753                                     stopHsbpManager();
2754                                 }
2755                                 return;
2756                             }
2757                             loadHsbpConfig();
2758                         });
2759                 }
2760             }
2761         });
2762 }
2763 /***************************** End of Section *******************************/
2764 
2765 /****************************************************************************/
2766 /***************** GPIO Events related Function Definitions *****************/
2767 /****************************************************************************/
nvmeLvc3AlertHandler()2768 static void nvmeLvc3AlertHandler()
2769 {
2770     /* If the state is not backplanesLoaded, we ignore the GPIO event as we
2771      * cannot communicate to the backplanes yet */
2772     if (appState < AppState::backplanesLoaded)
2773     {
2774         std::cerr << __FUNCTION__
2775                   << ": HSBP not initialized ! Dropping the interrupt ! \n";
2776         return;
2777     }
2778 
2779     /* This GPIO event only indicates the addition or removal of drive to either
2780      * of CPU. The backplanes detected need to be scanned and detect which drive
2781      * has been added/removed and enable/diable clock accordingly */
2782     gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
2783 
2784     if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
2785     {
2786         /* Check for HSBP Drives status to determine if any new drive has been
2787          * added/removed and update clocks accordingly */
2788         checkHsbpDrivesStatus();
2789     }
2790 
2791     nvmeLvc3AlertEvent.async_wait(
2792         boost::asio::posix::stream_descriptor::wait_read,
2793         [](const boost::system::error_code ec) {
2794             if (ec)
2795             {
2796                 std::cerr << __FUNCTION__ << ": nvmealert event error: "
2797                           << ec.message() << "\n";
2798             }
2799             nvmeLvc3AlertHandler();
2800         });
2801 }
2802 
hsbpRequestAlertGpioEvents(const std::string & name,const std::function<void ()> & handler,gpiod::line & gpioLine,boost::asio::posix::stream_descriptor & gpioEventDescriptor)2803 static bool hsbpRequestAlertGpioEvents(
2804     const std::string& name, const std::function<void()>& handler,
2805     gpiod::line& gpioLine,
2806     boost::asio::posix::stream_descriptor& gpioEventDescriptor)
2807 {
2808     // Find the GPIO line
2809     gpioLine = gpiod::find_line(name);
2810     if (!gpioLine)
2811     {
2812         std::cerr << __FUNCTION__ << ": Failed to find the " << name
2813                   << " line\n";
2814         return false;
2815     }
2816 
2817     try
2818     {
2819         gpioLine.request(
2820             {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
2821     }
2822     catch (std::exception&)
2823     {
2824         std::cerr << __FUNCTION__ << ": Failed to request events for " << name
2825                   << "\n";
2826         return false;
2827     }
2828 
2829     int gpioLineFd = gpioLine.event_get_fd();
2830     if (gpioLineFd < 0)
2831     {
2832         std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
2833         return false;
2834     }
2835 
2836     gpioEventDescriptor.assign(gpioLineFd);
2837 
2838     gpioEventDescriptor.async_wait(
2839         boost::asio::posix::stream_descriptor::wait_read,
2840         [&name, handler](const boost::system::error_code ec) {
2841             if (ec)
2842             {
2843                 std::cerr << __FUNCTION__ << ": " << name
2844                           << " fd handler error: " << ec.message() << "\n";
2845                 return;
2846             }
2847             handler();
2848         });
2849     return true;
2850 }
2851 /***************************** End of Section *******************************/
2852 
main()2853 int main()
2854 {
2855     std::cerr << "******* Starting hsbp-manager *******\n";
2856 
2857     /* Set the Dbus name */
2858     conn->request_name(busName);
2859 
2860     std::shared_ptr<sdbusplus::asio::dbus_interface> storageIface;
2861 
2862     /* Add interface for storage inventory */
2863     storageIface = objServer.add_interface(
2864         "/xyz/openbmc_project/inventory/item/storage/hsbp/1",
2865         "xyz.openbmc_project.Inventory.Item.Storage");
2866 
2867     storageIface->initialize();
2868 
2869     /* HSBP initializtion flow:
2870      * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
2871      * 2. Set up Dbus match for power - determine if host is up and running
2872      *    or powered off
2873      * 3. Set up Dbus match for HSBP backplanes and Drives
2874      * 4. Load HSBP config exposed by entity manager
2875      *    - Also setup a match to capture HSBP configuation in case
2876      *      entity-manager restarts
2877      * 5. Load Clock buffer and IO expander (and other peripherals if any
2878      *    related to HSBP functionality)
2879      *    - Reload the info each time HSBP configuration is changed
2880      * 6. Populate all Backpanes (HSBP's)
2881      * 7. Load all NVMe drive's and associate with HSBP Backpane
2882      */
2883 
2884     /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
2885     if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
2886                                     nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
2887                                     nvmeLvc3AlertEvent))
2888     {
2889         std::cerr << __FUNCTION__
2890                   << ": error: Unable to monitor events on HSBP "
2891                      "Alert line\n";
2892         return -1;
2893     }
2894 
2895     /* Setup Dbus-match for power */
2896     setupPowerMatch(conn);
2897 
2898     /* Setup Dbus-match for HSBP backplanes and Drives */
2899     setupBackplanesAndDrivesMatch();
2900 
2901     /* Setup HSBP Config match and load config
2902      * In the event of entity-manager reboot, the match will help catch new
2903      * configuration.
2904      * In the event of hsbp-manager reboot, loadHsbpConfig will get all
2905      * config details and will take care of remaining config's to be
2906      * loaded
2907      */
2908     setupHsbpConfigMatch();
2909     loadHsbpConfig();
2910 
2911     io.run();
2912     std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
2913     return -1;
2914 }
2915