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