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