xref: /openbmc/s2600wf-misc/hsbp-manager/src/hsbp_manager.cpp (revision 9cefb82d93b75f33f4f6f96fbcd1f358ce380395)
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                             if (std::holds_alternative<std::string>(value))
1703                             {
1704                                 assetInventory[key] =
1705                                     std::get<std::string>(value);
1706                             }
1707                         }
1708 
1709                         Backplane* parent = nullptr;
1710                         for (auto& [name, backplane] : backplanes)
1711                         {
1712                             muxIndex = 0;
1713                             for (const Mux& mux : *(backplane->muxes))
1714                             {
1715                                 if (bus == mux.bus && addr == mux.address)
1716                                 {
1717                                     parent = backplane.get();
1718                                     break;
1719                                 }
1720                                 muxIndex += mux.channels;
1721                             }
1722                             if (parent)
1723                             {
1724                                 /* Found the backplane. No need to proceed
1725                                  * further */
1726                                 break;
1727                             }
1728                         }
1729 
1730                         // assume its a M.2 or something without a hsbp
1731                         if (parent == nullptr)
1732                         {
1733                             std::string driveName =
1734                                 "Drive_" + std::to_string(getDriveCount() + 1);
1735                             auto& drive = ownerlessDrives.emplace_back(
1736                                 driveName, true, true, true, false);
1737                             drive.createAsset(assetInventory);
1738                             return;
1739                         }
1740 
1741                         driveIndex += muxIndex;
1742 
1743                         if (parent->drives.size() <= driveIndex)
1744                         {
1745                             std::cerr << __FUNCTION__
1746                                       << ": Illegal drive index at " << path
1747                                       << " " << driveIndex << "\n";
1748                             drivesLoadedCallback->setError();
1749                             return;
1750                         }
1751                         auto it = parent->drives.begin();
1752                         std::advance(it, driveIndex);
1753 
1754                         it->createAsset(assetInventory);
1755                     },
1756                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1757                     "" /*all interface items*/);
1758             }
1759         },
1760         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1761         0, std::array<const char*, 1>{nvmeIntf});
1762 }
1763 
populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,std::string & rootPath)1764 void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
1765                    std::string& rootPath)
1766 {
1767     const static std::array<const std::string, 4> muxTypes = {
1768         "xyz.openbmc_project.Configuration.PCA9543Mux",
1769         "xyz.openbmc_project.Configuration.PCA9544Mux",
1770         "xyz.openbmc_project.Configuration.PCA9545Mux",
1771         "xyz.openbmc_project.Configuration.PCA9546Mux"};
1772 
1773     conn->async_method_call(
1774         [muxes](const boost::system::error_code ec,
1775                 const GetSubTreeType& subtree) {
1776             if (ec)
1777             {
1778                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1779                           << ec.message() << "\n";
1780                 return;
1781             }
1782             size_t index = 0; // as we use a flat map, these are sorted
1783             for (const auto& [path, objDict] : subtree)
1784             {
1785                 if (objDict.empty() || objDict.begin()->second.empty())
1786                 {
1787                     continue;
1788                 }
1789 
1790                 const std::string& owner = objDict.begin()->first;
1791                 const std::vector<std::string>& interfaces =
1792                     objDict.begin()->second;
1793 
1794                 const std::string* interface = nullptr;
1795                 for (const std::string& iface : interfaces)
1796                 {
1797                     if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
1798                         muxTypes.end())
1799                     {
1800                         interface = &iface;
1801                         break;
1802                     }
1803                 }
1804 
1805                 if (interface == nullptr)
1806                 {
1807                     std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
1808                     continue;
1809                 }
1810 
1811                 conn->async_method_call(
1812                     [path, muxes, index](
1813                         const boost::system::error_code ec2,
1814                         const boost::container::flat_map<
1815                             std::string,
1816                             std::variant<uint64_t, std::vector<std::string>>>&
1817                             values) {
1818                         if (ec2)
1819                         {
1820                             std::cerr << __FUNCTION__
1821                                       << ": Error Getting Config "
1822                                       << ec2.message() << "\n";
1823                             return;
1824                         }
1825                         auto findBus = values.find("Bus");
1826                         auto findAddress = values.find("Address");
1827                         auto findChannelNames = values.find("ChannelNames");
1828                         if (findBus == values.end() ||
1829                             findAddress == values.end())
1830                         {
1831                             std::cerr << __FUNCTION__
1832                                       << ": Illegal configuration at " << path
1833                                       << "\n";
1834                             return;
1835                         }
1836                         size_t bus = static_cast<size_t>(
1837                             std::get<uint64_t>(findBus->second));
1838                         size_t address = static_cast<size_t>(
1839                             std::get<uint64_t>(findAddress->second));
1840                         std::vector<std::string> channels =
1841                             std::get<std::vector<std::string>>(
1842                                 findChannelNames->second);
1843                         muxes->emplace(bus, address, channels.size(), index);
1844                     },
1845                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1846                     *interface);
1847                 index++;
1848             }
1849         },
1850         mapper::busName, mapper::path, mapper::interface, mapper::subtree,
1851         rootPath, 1, muxTypes);
1852 }
1853 
populateHsbpBackplanes(const std::shared_ptr<AsyncCallbackHandler> & backplanesLoadedCallback)1854 void populateHsbpBackplanes(
1855     const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
1856 {
1857     std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
1858     appState = AppState::loadingBackplanes;
1859     backplanes.clear();
1860 
1861     conn->async_method_call(
1862         [backplanesLoadedCallback](const boost::system::error_code ec,
1863                                    const GetSubTreeType& subtree) {
1864             if (ec)
1865             {
1866                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1867                           << ec.message() << "\n";
1868                 backplanesLoadedCallback->setError();
1869                 return;
1870             }
1871 
1872             if (subtree.empty())
1873             {
1874                 /* There wer no HSBP's detected. set teh state back to
1875                  * componentsLoaded so that on backplane match event, the
1876                  * process can start again */
1877                 appState = AppState::componentsLoaded;
1878                 std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
1879                 return;
1880             }
1881 
1882             for (const auto& [path, objDict] : subtree)
1883             {
1884                 if (objDict.empty())
1885                 {
1886                     std::cerr << __FUNCTION__
1887                               << ": Subtree data "
1888                                  "corrupted !\n";
1889                     backplanesLoadedCallback->setError();
1890                     return;
1891                 }
1892 
1893                 const std::string& owner = objDict.begin()->first;
1894                 conn->async_method_call(
1895                     [backplanesLoadedCallback, path,
1896                      owner](const boost::system::error_code ec2,
1897                             const boost::container::flat_map<
1898                                 std::string, BasicVariantType>& resp) {
1899                         if (ec2)
1900                         {
1901                             std::cerr << __FUNCTION__
1902                                       << ": Error Getting Config "
1903                                       << ec2.message() << "\n";
1904                             backplanesLoadedCallback->setError();
1905                             return;
1906                         }
1907                         std::optional<size_t> bus;
1908                         std::optional<size_t> address;
1909                         std::optional<size_t> backplaneIndex;
1910                         std::optional<std::string> name;
1911                         for (const auto& [key, value] : resp)
1912                         {
1913                             if (key == "Bus")
1914                             {
1915                                 bus = std::get<uint64_t>(value);
1916                             }
1917                             else if (key == "Address")
1918                             {
1919                                 address = std::get<uint64_t>(value);
1920                             }
1921                             else if (key == "Index")
1922                             {
1923                                 backplaneIndex = std::get<uint64_t>(value);
1924                             }
1925                             else if (key == "Name")
1926                             {
1927                                 name = std::get<std::string>(value);
1928                             }
1929                         }
1930                         if (!bus || !address || !name || !backplaneIndex)
1931                         {
1932                             std::cerr << __FUNCTION__
1933                                       << ": Illegal configuration at " << path
1934                                       << "\n";
1935                             backplanesLoadedCallback->setError();
1936                             return;
1937                         }
1938                         std::string parentPath =
1939                             std::filesystem::path(path).parent_path();
1940                         const auto& [backplane, status] = backplanes.emplace(
1941                             *name, std::make_shared<Backplane>(
1942                                        *bus, *address, *backplaneIndex, *name));
1943                         backplane->second->run(parentPath, owner);
1944                         populateMuxes(backplane->second->muxes, parentPath);
1945                     },
1946                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1947                     hsbpCpldInft);
1948             }
1949         },
1950         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1951         0, std::array<const char*, 1>{hsbpCpldInft});
1952 }
1953 
setUpBackplanesAndDrives()1954 void setUpBackplanesAndDrives()
1955 {
1956     static bool backplanesScanInProgress = false;
1957     static bool backplanesRescanInQueue = false;
1958 
1959     if (appState < AppState::componentsLoaded)
1960     {
1961         std::cerr << __FUNCTION__
1962                   << ": Components are not initialized ! Cancelling scan of "
1963                      "Backplanes ! \n";
1964         return;
1965     }
1966 
1967     if (backplanesScanInProgress)
1968     {
1969         std::cerr << __FUNCTION__
1970                   << ": Backplanes Scan is already in progress\n";
1971         if (backplanesRescanInQueue)
1972         {
1973             /* There is already a Re-Scan in queue. No need to create multiple
1974              * rescans */
1975             return;
1976         }
1977 
1978         backplanesRescanInQueue = true;
1979 
1980         std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
1981 
1982         auto backplaneScanTimer =
1983             std::make_shared<boost::asio::steady_timer>(io);
1984         backplaneScanTimer->expires_after(std::chrono::seconds(1));
1985         backplaneScanTimer->async_wait(
1986             [backplaneScanTimer](const boost::system::error_code ec) {
1987                 if (ec == boost::asio::error::operation_aborted)
1988                 {
1989                     // Timer was Aborted
1990                     return;
1991                 }
1992                 else if (ec)
1993                 {
1994                     std::cerr << "backplaneScanTimer: Timer error"
1995                               << ec.message() << "\n";
1996                     return;
1997                 }
1998                 backplanesRescanInQueue = false;
1999                 setUpBackplanesAndDrives();
2000             });
2001 
2002         return;
2003     }
2004 
2005     backplanesScanInProgress = true;
2006 
2007     /* Set Callback to be called once backplanes are populated to call
2008      * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
2009     auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
2010         []() {
2011             /* If no HSBP's were detected, the state changes to
2012              * componentsLoaded. Proceed further only if state was
2013              * loadingBackplanes */
2014             if (appState != AppState::loadingBackplanes)
2015             {
2016                 backplanesScanInProgress = false;
2017                 return;
2018             }
2019 
2020             /* If there is a ReScan in the Queue, dont proceed further. Load the
2021              * Backplanes again and then proceed further */
2022             if (backplanesRescanInQueue)
2023             {
2024                 backplanesScanInProgress = false;
2025                 return;
2026             }
2027 
2028             appState = AppState::backplanesLoaded;
2029             std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
2030 
2031             checkHsbpDrivesStatus();
2032             updateAssets();
2033             backplanesScanInProgress = false;
2034         },
2035         []() {
2036             /* Loading Backplanes is an important step. If the load failed due
2037              * to an error, stop the app so that restart cant be triggerred */
2038             std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
2039             appState = AppState::idle;
2040             backplanesScanInProgress = false;
2041             stopHsbpManager();
2042         });
2043 
2044     populateHsbpBackplanes(backplanesLoadedCallback);
2045 }
2046 
setupBackplanesAndDrivesMatch()2047 void setupBackplanesAndDrivesMatch()
2048 {
2049     static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
2050         *conn,
2051         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2052         "member='PropertiesChanged', "
2053         "interface='org.freedesktop.DBus.Properties', "
2054         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2055             std::string(hsbpCpldInft) + "'",
2056         [](sdbusplus::message_t& msg) {
2057             std::string intfName;
2058             boost::container::flat_map<std::string, BasicVariantType> values;
2059             msg.read(intfName, values);
2060 
2061             /* This match will be triggered for each of the property being set
2062              * under the hsbpCpldInft interface. Call the loader only on one
2063              * property say "name". This will avoid multiple calls to populate
2064              * function
2065              */
2066             for (const auto& [key, value] : values)
2067             {
2068                 if (key == "Name")
2069                 {
2070                     /* This match will be triggered when ever there is a
2071                      * addition/removal of HSBP backplane. At this stage, all
2072                      * the HSBP's need to be populated again and also assets
2073                      * have to be re-discovered. So, setting state to
2074                      * componentsLoaded and calling setUpBackplanesAndDrives()
2075                      * only if configuration and components loading was
2076                      * completed */
2077                     if (appState < AppState::componentsLoaded)
2078                     {
2079                         /* Configuration is not loaded yet. Backplanes will be
2080                          * loaded
2081                          * once configuration and components are loaded. */
2082                         std::cerr << __FUNCTION__
2083                                   << ": Discarding Backplane match\n";
2084                         return;
2085                     }
2086 
2087                     appState = AppState::componentsLoaded;
2088 
2089                     /* We will call the function after a small delay to let all
2090                      * the properties to be intialized */
2091                     auto backplaneTimer =
2092                         std::make_shared<boost::asio::steady_timer>(io);
2093                     backplaneTimer->expires_after(std::chrono::seconds(2));
2094                     backplaneTimer->async_wait(
2095                         [backplaneTimer](const boost::system::error_code ec) {
2096                             if (ec == boost::asio::error::operation_aborted)
2097                             {
2098                                 return;
2099                             }
2100                             else if (ec)
2101                             {
2102                                 std::cerr << "backplaneTimer: Timer error"
2103                                           << ec.message() << "\n";
2104                                 return;
2105                             }
2106                             setUpBackplanesAndDrives();
2107                         });
2108                 }
2109             }
2110         });
2111 
2112     static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
2113         *conn,
2114         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2115         "member='PropertiesChanged', "
2116         "interface='org.freedesktop.DBus.Properties', arg0='" +
2117             std::string(nvmeIntf) + "'",
2118         [](sdbusplus::message_t& msg) {
2119             std::string intfName;
2120             boost::container::flat_map<std::string, BasicVariantType> values;
2121             msg.read(intfName, values);
2122 
2123             /* This match will be triggered for each of the property being set
2124              * under the nvmeIntf interface. Call the loader only on one
2125              * property say "name". This will avoid multiple calls to populate
2126              * function
2127              */
2128             for (const auto& [key, value] : values)
2129             {
2130                 if (key == "Name")
2131                 {
2132                     /* This match will be triggered when ever there is a
2133                      * addition/removal of drives. At this stage only assets
2134                      * have to be re-discovered. So, setting state to
2135                      * backplanesLoaded and calling updateAssets() only if all
2136                      * previous states are completed */
2137                     if (appState < AppState::backplanesLoaded)
2138                     {
2139                         /* Configuration is not loaded yet. Drives will be
2140                          * loaded once
2141                          * configuration, components and backplanes are loaded.
2142                          */
2143                         std::cerr << __FUNCTION__
2144                                   << ": Discarding Drive match\n";
2145                         return;
2146                     }
2147 
2148                     appState = AppState::backplanesLoaded;
2149 
2150                     /* We will call the function after a small delay to let all
2151                      * the properties to be intialized */
2152                     auto driveTimer =
2153                         std::make_shared<boost::asio::steady_timer>(io);
2154                     driveTimer->expires_after(std::chrono::seconds(2));
2155                     driveTimer->async_wait(
2156                         [driveTimer](const boost::system::error_code ec) {
2157                             if (ec == boost::asio::error::operation_aborted)
2158                             {
2159                                 return;
2160                             }
2161                             else if (ec)
2162                             {
2163                                 std::cerr << "driveTimer: Timer error"
2164                                           << ec.message() << "\n";
2165                                 return;
2166                             }
2167                             updateAssets();
2168                         });
2169                 }
2170             }
2171         });
2172 }
2173 /***************************** End of Section *******************************/
2174 
2175 /****************************************************************************/
2176 /******************* Components related Function Definitions ****************/
2177 /****************************************************************************/
verifyComponentsLoaded()2178 bool verifyComponentsLoaded()
2179 {
2180     std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
2181 
2182     /* Loop through all clock buffers */
2183     for (auto& clockBuffer : clockBuffers)
2184     {
2185         if (!clockBuffer.isInitialized())
2186         {
2187             std::cerr << "Critical Error: Initializing \""
2188                       << clockBuffer.getName() << "\" failed\n";
2189             return false;
2190         }
2191     }
2192 
2193     /* Loop through all IO Expanders */
2194     for (auto& ioExpander : ioExpanders)
2195     {
2196         if (!ioExpander.isInitialized())
2197         {
2198             std::cerr << "Critical Error: Initializing \""
2199                       << ioExpander.getName() << "\" failed\n";
2200             return false;
2201         }
2202     }
2203 
2204     std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
2205 
2206     return true;
2207 }
2208 /***************************** End of Section *******************************/
2209 
2210 /****************************************************************************/
2211 /****************** IO expander related Function Definitions ****************/
2212 /****************************************************************************/
loadIoExpanderInfo(const std::shared_ptr<AsyncCallbackHandler> & componentsLoadedCallback)2213 void loadIoExpanderInfo(
2214     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2215 {
2216     appState = AppState::loadingComponents;
2217 
2218     /* Clear global ioExpanders to start off */
2219     ioExpanders.clear();
2220 
2221     conn->async_method_call(
2222         [componentsLoadedCallback](const boost::system::error_code ec,
2223                                    const GetSubTreeType& subtree) {
2224             if (ec)
2225             {
2226                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2227                           << ec.message() << "\n";
2228                 componentsLoadedCallback->setError();
2229                 return;
2230             }
2231 
2232             for (auto& [path, objDict] : subtree)
2233             {
2234 
2235                 if (objDict.empty())
2236                 {
2237                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2238                     componentsLoadedCallback->setError();
2239                     return;
2240                 }
2241 
2242                 /* Ideally there would be only one element in objDict as only
2243                  * one service exposes it and there would be only one interface
2244                  * so it is safe to directly read them without loop */
2245                 const std::string& service = objDict.begin()->first;
2246                 const std::string& intf = objDict.begin()->second.front();
2247 
2248                 conn->async_method_call(
2249                     [componentsLoadedCallback](
2250                         const boost::system::error_code er,
2251                         const boost::container::flat_map<
2252                             std::string, BasicVariantType>& resp) {
2253                         if (er)
2254                         {
2255                             std::cerr << __FUNCTION__
2256                                       << ": Error Getting "
2257                                          "Config "
2258                                       << er.message() << "\n";
2259                             componentsLoadedCallback->setError();
2260                             return;
2261                         }
2262 
2263                         std::optional<uint64_t> bus;
2264                         std::optional<uint64_t> address;
2265                         std::optional<uint64_t> confIORegAddr;
2266                         std::optional<uint64_t> outCtrlBaseAddr;
2267                         std::optional<uint64_t> outCtrlByteCount;
2268                         std::unordered_map<std::string,
2269                                            std::vector<std::string>>
2270                             ioMap;
2271                         std::optional<std::string> name;
2272                         std::optional<std::string> type;
2273 
2274                         /* Loop through to get all IO Expander properties */
2275                         for (const auto& [key, value] : resp)
2276                         {
2277                             if (key == "Bus")
2278                             {
2279                                 bus = std::get<uint64_t>(value);
2280                             }
2281                             else if (key == "Address")
2282                             {
2283                                 address = std::get<uint64_t>(value);
2284                             }
2285                             else if (key == "ConfIORegAddr")
2286                             {
2287                                 confIORegAddr = std::get<uint64_t>(value);
2288                             }
2289                             else if (key == "OutCtrlBaseAddr")
2290                             {
2291                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2292                             }
2293                             else if (key == "OutCtrlByteCount")
2294                             {
2295                                 outCtrlByteCount = std::get<uint64_t>(value);
2296                             }
2297                             else if (key == "Name")
2298                             {
2299                                 name = std::get<std::string>(value);
2300                             }
2301                             else if (key == "Type")
2302                             {
2303                                 type = std::get<std::string>(value);
2304                             }
2305                             else if (key.starts_with("IO"))
2306                             {
2307                                 std::optional<std::vector<std::string>> outList;
2308                                 outList = std::get<NvmeMapping>(value);
2309                                 if (!outList)
2310                                 {
2311                                     break;
2312                                 }
2313                                 ioMap.try_emplace(key, *outList);
2314                             }
2315                         }
2316 
2317                         /* Verify if all properties were defined */
2318                         if (!bus || !address || !confIORegAddr ||
2319                             !outCtrlBaseAddr || !outCtrlByteCount || !name)
2320                         {
2321                             std::cerr << __FUNCTION__
2322                                       << ": Incomplete "
2323                                          "Clock Buffer definition !! \n";
2324                             componentsLoadedCallback->setError();
2325                             return;
2326                         }
2327 
2328                         /* Check if we were able to get byteMap correctly */
2329                         if ((*outCtrlByteCount) != ioMap.size())
2330                         {
2331                             std::cerr << "loadIoExpanderInfo(): Incomplete "
2332                                          "IO Map !! \n";
2333                             componentsLoadedCallback->setError();
2334                             return;
2335                         }
2336 
2337                         /* Create IO expander object and add it to global
2338                          * ioExpanders vector */
2339                         ioExpanders.emplace_front(
2340                             *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
2341                             *outCtrlByteCount, ioMap, *name, *type);
2342                     },
2343                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2344                     intf);
2345             }
2346         },
2347         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2348         0, hsbpConfig.ioExpanderTypes);
2349 }
2350 /***************************** End of Section *******************************/
2351 
2352 /****************************************************************************/
2353 /***************** Clock buffer related Function Definitions ****************/
2354 /****************************************************************************/
loadClockBufferInfo(const std::shared_ptr<AsyncCallbackHandler> & componentsLoadedCallback)2355 void loadClockBufferInfo(
2356     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2357 {
2358     appState = AppState::loadingComponents;
2359 
2360     /* Clear global clockBuffers to start off */
2361     clockBuffers.clear();
2362 
2363     conn->async_method_call(
2364         [componentsLoadedCallback](const boost::system::error_code ec,
2365                                    const GetSubTreeType& subtree) {
2366             if (ec)
2367             {
2368                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2369                           << ec.message() << "\n";
2370                 componentsLoadedCallback->setError();
2371                 return;
2372             }
2373 
2374             for (auto& [path, objDict] : subtree)
2375             {
2376 
2377                 if (objDict.empty())
2378                 {
2379                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2380                     componentsLoadedCallback->setError();
2381                     return;
2382                 }
2383 
2384                 /* Ideally there would be only one element in objDict as only
2385                  * one service exposes it and there would be only one interface
2386                  * so it is safe to directly read them without loop */
2387                 const std::string& service = objDict.begin()->first;
2388                 const std::string& intf = objDict.begin()->second.front();
2389 
2390                 conn->async_method_call(
2391                     [componentsLoadedCallback](
2392                         const boost::system::error_code er,
2393                         const boost::container::flat_map<
2394                             std::string, BasicVariantType>& resp) {
2395                         if (er)
2396                         {
2397                             std::cerr << __FUNCTION__
2398                                       << ": Error Getting "
2399                                          "Config "
2400                                       << er.message() << "\n";
2401                             componentsLoadedCallback->setError();
2402                             return;
2403                         }
2404 
2405                         std::optional<uint64_t> bus;
2406                         std::optional<uint64_t> address;
2407                         std::optional<std::string> mode;
2408                         std::optional<uint64_t> outCtrlBaseAddr;
2409                         std::optional<uint64_t> outCtrlByteCount;
2410                         std::unordered_map<std::string,
2411                                            std::vector<std::string>>
2412                             byteMap;
2413                         std::optional<std::string> name;
2414                         std::optional<std::string> type;
2415 
2416                         /* Loop through to get all Clock Buffer properties */
2417                         for (const auto& [key, value] : resp)
2418                         {
2419                             if (key == "Bus")
2420                             {
2421                                 bus = std::get<uint64_t>(value);
2422                             }
2423                             else if (key == "Address")
2424                             {
2425                                 address = std::get<uint64_t>(value);
2426                             }
2427                             else if (key == "Mode")
2428                             {
2429                                 mode = std::get<std::string>(value);
2430                             }
2431                             else if (key == "OutCtrlBaseAddr")
2432                             {
2433                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2434                             }
2435                             else if (key == "OutCtrlByteCount")
2436                             {
2437                                 outCtrlByteCount = std::get<uint64_t>(value);
2438                             }
2439                             else if (key == "Name")
2440                             {
2441                                 name = std::get<std::string>(value);
2442                             }
2443                             else if (key == "Type")
2444                             {
2445                                 type = std::get<std::string>(value);
2446                             }
2447                             else if (key.starts_with("Byte"))
2448                             {
2449                                 std::optional<std::vector<std::string>>
2450                                     byteList;
2451                                 byteList = std::get<NvmeMapping>(value);
2452                                 if (!byteList)
2453                                 {
2454                                     break;
2455                                 }
2456                                 byteMap.try_emplace(key, *byteList);
2457                             }
2458                         }
2459 
2460                         /* Verify if all properties were defined */
2461                         if (!bus || !address || !mode || !outCtrlBaseAddr ||
2462                             !outCtrlByteCount || !name)
2463                         {
2464                             std::cerr << __FUNCTION__
2465                                       << ": Incomplete "
2466                                          "Clock Buffer definition !! \n";
2467                             componentsLoadedCallback->setError();
2468                             return;
2469                         }
2470 
2471                         /* Check if we were able to get byteMap correctly */
2472                         if ((*outCtrlByteCount) != byteMap.size())
2473                         {
2474                             std::cerr << __FUNCTION__
2475                                       << ": Incomplete "
2476                                          "Byte Map !! \n";
2477                             componentsLoadedCallback->setError();
2478                             return;
2479                         }
2480 
2481                         /* Create clock buffer object and add it to global
2482                          * clockBuffers vector */
2483                         clockBuffers.emplace_front(
2484                             *bus, *address, *mode, *outCtrlBaseAddr,
2485                             *outCtrlByteCount, byteMap, *name, *type);
2486                     },
2487                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2488                     intf);
2489             }
2490         },
2491         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2492         0, hsbpConfig.clockBufferTypes);
2493 }
2494 /***************************** End of Section *******************************/
2495 
2496 /****************************************************************************/
2497 /***************** HSBP Config related Function Definitions *****************/
2498 /****************************************************************************/
loadHsbpConfig()2499 void loadHsbpConfig()
2500 {
2501     appState = AppState::loadingHsbpConfig;
2502 
2503     conn->async_method_call(
2504         [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
2505             if (ec)
2506             {
2507                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2508                           << ec.message() << "\n";
2509                 return;
2510             }
2511 
2512             if (subtree.empty())
2513             {
2514                 /* Entity manager is either still loading the configuration or
2515                  * failed to load. In either way, return from here as the dbus
2516                  * match will take care */
2517                 std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
2518                 return;
2519             }
2520 
2521             /* There should be only one HSBP Configureation exposed */
2522             if (subtree.size() != 1)
2523             {
2524                 std::cerr << __FUNCTION__
2525                           << ": Multiple configurations "
2526                              "detected !!\n";
2527                 /* Critical Error. Stop Application */
2528                 stopHsbpManager();
2529                 return;
2530             }
2531 
2532             auto& path = subtree.begin()->first;
2533             auto& objDict = subtree.begin()->second;
2534 
2535             if (objDict.empty())
2536             {
2537                 /* Critical Error. Stop Application */
2538                 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2539                 stopHsbpManager();
2540                 return;
2541             }
2542 
2543             const std::string& service = objDict.begin()->first;
2544 
2545             conn->async_method_call(
2546                 [](const boost::system::error_code er,
2547                    const boost::container::flat_map<std::string,
2548                                                     BasicVariantType>& resp) {
2549                     if (er)
2550                     {
2551                         std::cerr << __FUNCTION__ << ": Error Getting Config "
2552                                   << er.message() << "\n";
2553                         /* Critical Error. Stop Application */
2554                         stopHsbpManager();
2555                         return;
2556                     }
2557 
2558                     std::optional<uint64_t> rootI2cBus;
2559                     std::optional<std::vector<std::string>> supportedHsbps;
2560                     std::optional<std::vector<std::string>> clockBufferTypes;
2561                     std::optional<std::vector<std::string>> ioExpanderTypes;
2562 
2563                     /* Loop through to get root i2c bus and list of supported
2564                      * HSBPs */
2565                     for (const auto& [key, value] : resp)
2566                     {
2567                         if (key == "HsbpSupported")
2568                         {
2569                             supportedHsbps =
2570                                 std::get<std::vector<std::string>>(value);
2571                         }
2572                         else if (key == "RootI2cBus")
2573                         {
2574                             rootI2cBus = std::get<uint64_t>(value);
2575                         }
2576                         else if (key == "ClockBuffer")
2577                         {
2578                             clockBufferTypes =
2579                                 std::get<std::vector<std::string>>(value);
2580                         }
2581                         else if (key == "IoExpander")
2582                         {
2583                             ioExpanderTypes =
2584                                 std::get<std::vector<std::string>>(value);
2585                         }
2586                     }
2587 
2588                     /* Verify if i2c bus, supported HSBP's and clock buffers
2589                      * were defined (IO Expanders are optional) */
2590                     if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
2591                     {
2592                         std::cerr << __FUNCTION__
2593                                   << ": Incomplete HSBP "
2594                                      "configuration !! \n";
2595                         /* Critical Error. Stop Application */
2596                         stopHsbpManager();
2597                         return;
2598                     }
2599 
2600                     /* Clear and Load all details to global hsbp configuration
2601                      * variable */
2602                     hsbpConfig.clearConfig();
2603                     hsbpConfig.rootBus = *rootI2cBus;
2604                     hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
2605 
2606                     for (auto& clkBuffType : *clockBufferTypes)
2607                     {
2608                         hsbpConfig.clockBufferTypes.emplace_back(
2609                             "xyz.openbmc_project.Configuration." + clkBuffType);
2610                     }
2611 
2612                     if (ioExpanderTypes)
2613                     {
2614                         for (auto& ioCntrType : *ioExpanderTypes)
2615                         {
2616                             hsbpConfig.ioExpanderTypes.emplace_back(
2617                                 "xyz.openbmc_project.Configuration." +
2618                                 ioCntrType);
2619                         }
2620                     }
2621 
2622                     /* Loop through to get HSBP-NVME map and Components map
2623                      * details */
2624                     uint8_t hsbpMapCount = 0;
2625                     for (const auto& [key, value] : resp)
2626                     {
2627                         if (std::find(hsbpConfig.supportedHsbps.begin(),
2628                                       hsbpConfig.supportedHsbps.end(),
2629                                       key) != hsbpConfig.supportedHsbps.end())
2630                         {
2631                             std::optional<std::vector<std::string>> hsbpMap;
2632                             hsbpMap = std::get<NvmeMapping>(value);
2633                             if (!hsbpMap)
2634                             {
2635                                 break;
2636                             }
2637                             hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
2638                             hsbpMapCount++;
2639                         }
2640                     }
2641 
2642                     /* Check if we were able to get all the HSBP-NVMe maps */
2643                     if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
2644                     {
2645                         std::cerr << __FUNCTION__
2646                                   << ": Incomplete HSBP Map "
2647                                      "details !! \n";
2648                         /* Critical Error. Stop Application */
2649                         stopHsbpManager();
2650                         return;
2651                     }
2652 
2653                     /* HSBP configuration is loaded */
2654                     appState = AppState::hsbpConfigLoaded;
2655                     std::cerr << "HSBP Config loaded !\n";
2656 
2657                     /* Get Clock buffers and IO expander details. Create shared
2658                      * object of AsyncCallbackHandler with success and error
2659                      * callback */
2660                     auto componentsLoadedCallback = std::make_shared<
2661                         AsyncCallbackHandler>(
2662                         []() {
2663                             /* Verify if all components were initialized without
2664                              * errors */
2665                             if (!verifyComponentsLoaded())
2666                             {
2667                                 /* The application cannot proceed further as
2668                                  * components initialization failed. App needs
2669                                  * Restart */
2670                                 appState = AppState::idle;
2671                                 std::cerr
2672                                     << "One or more Componenets initialization "
2673                                        "failed !! Restart Required !\n";
2674                                 stopHsbpManager();
2675                             }
2676 
2677                             appState = AppState::componentsLoaded;
2678                             setUpBackplanesAndDrives();
2679                         },
2680                         []() {
2681                             /* The application cannot proceed further as
2682                              * components load failed. App needs Restart */
2683                             appState = AppState::idle;
2684                             std::cerr << "Loading Componenets failed !! "
2685                                          "Restart Required !\n";
2686                             stopHsbpManager();
2687                         });
2688 
2689                     loadClockBufferInfo(componentsLoadedCallback);
2690 
2691                     if (ioExpanderTypes)
2692                     {
2693                         loadIoExpanderInfo(componentsLoadedCallback);
2694                     }
2695                 },
2696                 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2697                 hsbpConfigIntf);
2698         },
2699         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2700         0, std::array<const char*, 1>{hsbpConfigIntf});
2701 }
2702 
setupHsbpConfigMatch()2703 void setupHsbpConfigMatch()
2704 {
2705     static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
2706         *conn,
2707         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2708         "member='PropertiesChanged', "
2709         "interface='org.freedesktop.DBus.Properties', "
2710         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2711             std::string(hsbpConfigIntf) + "'",
2712         [](sdbusplus::message_t& msg) {
2713             std::string intfName;
2714             boost::container::flat_map<std::string, BasicVariantType> values;
2715             msg.read(intfName, values);
2716 
2717             /* This match will be triggered for each of the property being set
2718              * under the hsbpConfig interface. "HsbpSupported" is one of the
2719              * important property which will enable us to read other properties.
2720              * So, when the match event occurs for "HsbpSupported" property
2721              * being set, we will call "loadHsbpConfig()" If the control has
2722              * come here, its either the first initialization or entity-manager
2723              * reload. So, we will reset the state to uninitialized
2724              */
2725             for (const auto& [key, value] : values)
2726             {
2727                 if (key == "HsbpSupported")
2728                 {
2729                     /* Configuration change detected, change the state to stop
2730                      * other processing */
2731                     appState = AppState::idle;
2732 
2733                     /* We will call the function after a small delay to let all
2734                      * the properties to be intialized */
2735                     auto loadTimer =
2736                         std::make_shared<boost::asio::steady_timer>(io);
2737                     loadTimer->expires_after(std::chrono::seconds(1));
2738                     loadTimer->async_wait(
2739                         [loadTimer](const boost::system::error_code ec) {
2740                             if (ec == boost::asio::error::operation_aborted)
2741                             {
2742                                 return;
2743                             }
2744                             else if (ec)
2745                             {
2746                                 std::cerr << __FUNCTION__ << ": Timer error"
2747                                           << ec.message() << "\n";
2748                                 if (hsbpConfig.supportedHsbps.empty())
2749                                 {
2750                                     /* Critical Error as none of the
2751                                      * configuration was loaded and timer
2752                                      * failed. Stop the application */
2753                                     stopHsbpManager();
2754                                 }
2755                                 return;
2756                             }
2757                             loadHsbpConfig();
2758                         });
2759                 }
2760             }
2761         });
2762 }
2763 /***************************** End of Section *******************************/
2764 
2765 /****************************************************************************/
2766 /***************** GPIO Events related Function Definitions *****************/
2767 /****************************************************************************/
nvmeLvc3AlertHandler()2768 static void nvmeLvc3AlertHandler()
2769 {
2770     /* If the state is not backplanesLoaded, we ignore the GPIO event as we
2771      * cannot communicate to the backplanes yet */
2772     if (appState < AppState::backplanesLoaded)
2773     {
2774         std::cerr << __FUNCTION__
2775                   << ": HSBP not initialized ! Dropping the interrupt ! \n";
2776         return;
2777     }
2778 
2779     /* This GPIO event only indicates the addition or removal of drive to either
2780      * of CPU. The backplanes detected need to be scanned and detect which drive
2781      * has been added/removed and enable/diable clock accordingly */
2782     gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
2783 
2784     if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
2785     {
2786         /* Check for HSBP Drives status to determine if any new drive has been
2787          * added/removed and update clocks accordingly */
2788         checkHsbpDrivesStatus();
2789     }
2790 
2791     nvmeLvc3AlertEvent.async_wait(
2792         boost::asio::posix::stream_descriptor::wait_read,
2793         [](const boost::system::error_code ec) {
2794             if (ec)
2795             {
2796                 std::cerr << __FUNCTION__
2797                           << ": nvmealert event error: " << ec.message()
2798                           << "\n";
2799             }
2800             nvmeLvc3AlertHandler();
2801         });
2802 }
2803 
hsbpRequestAlertGpioEvents(const std::string & name,const std::function<void ()> & handler,gpiod::line & gpioLine,boost::asio::posix::stream_descriptor & gpioEventDescriptor)2804 static bool hsbpRequestAlertGpioEvents(
2805     const std::string& name, const std::function<void()>& handler,
2806     gpiod::line& gpioLine,
2807     boost::asio::posix::stream_descriptor& gpioEventDescriptor)
2808 {
2809     // Find the GPIO line
2810     gpioLine = gpiod::find_line(name);
2811     if (!gpioLine)
2812     {
2813         std::cerr << __FUNCTION__ << ": Failed to find the " << name
2814                   << " line\n";
2815         return false;
2816     }
2817 
2818     try
2819     {
2820         gpioLine.request(
2821             {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
2822     }
2823     catch (std::exception&)
2824     {
2825         std::cerr << __FUNCTION__ << ": Failed to request events for " << name
2826                   << "\n";
2827         return false;
2828     }
2829 
2830     int gpioLineFd = gpioLine.event_get_fd();
2831     if (gpioLineFd < 0)
2832     {
2833         std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
2834         return false;
2835     }
2836 
2837     gpioEventDescriptor.assign(gpioLineFd);
2838 
2839     gpioEventDescriptor.async_wait(
2840         boost::asio::posix::stream_descriptor::wait_read,
2841         [&name, handler](const boost::system::error_code ec) {
2842             if (ec)
2843             {
2844                 std::cerr << __FUNCTION__ << ": " << name
2845                           << " fd handler error: " << ec.message() << "\n";
2846                 return;
2847             }
2848             handler();
2849         });
2850     return true;
2851 }
2852 /***************************** End of Section *******************************/
2853 
main()2854 int main()
2855 {
2856     std::cerr << "******* Starting hsbp-manager *******\n";
2857 
2858     /* Set the Dbus name */
2859     conn->request_name(busName);
2860 
2861     std::shared_ptr<sdbusplus::asio::dbus_interface> storageIface;
2862 
2863     /* Add interface for storage inventory */
2864     storageIface = objServer.add_interface(
2865         "/xyz/openbmc_project/inventory/item/storage/hsbp/1",
2866         "xyz.openbmc_project.Inventory.Item.Storage");
2867 
2868     storageIface->initialize();
2869 
2870     /* HSBP initializtion flow:
2871      * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
2872      * 2. Set up Dbus match for power - determine if host is up and running
2873      *    or powered off
2874      * 3. Set up Dbus match for HSBP backplanes and Drives
2875      * 4. Load HSBP config exposed by entity manager
2876      *    - Also setup a match to capture HSBP configuation in case
2877      *      entity-manager restarts
2878      * 5. Load Clock buffer and IO expander (and other peripherals if any
2879      *    related to HSBP functionality)
2880      *    - Reload the info each time HSBP configuration is changed
2881      * 6. Populate all Backpanes (HSBP's)
2882      * 7. Load all NVMe drive's and associate with HSBP Backpane
2883      */
2884 
2885     /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
2886     if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
2887                                     nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
2888                                     nvmeLvc3AlertEvent))
2889     {
2890         std::cerr << __FUNCTION__
2891                   << ": error: Unable to monitor events on HSBP "
2892                      "Alert line\n";
2893         return -1;
2894     }
2895 
2896     /* Setup Dbus-match for power */
2897     setupPowerMatch(conn);
2898 
2899     /* Setup Dbus-match for HSBP backplanes and Drives */
2900     setupBackplanesAndDrivesMatch();
2901 
2902     /* Setup HSBP Config match and load config
2903      * In the event of entity-manager reboot, the match will help catch new
2904      * configuration.
2905      * In the event of hsbp-manager reboot, loadHsbpConfig will get all
2906      * config details and will take care of remaining config's to be
2907      * loaded
2908      */
2909     setupHsbpConfigMatch();
2910     loadHsbpConfig();
2911 
2912     io.run();
2913     std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
2914     return -1;
2915 }
2916