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