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