blob: 4f3f3c5bbd3d20c3ffd4dbec19eb369eeb4b8b4e [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
19#include <boost/algorithm/string/replace.hpp>
Rohit Chandel52639be2021-04-14 15:10:41 +053020#include <boost/asio/posix/stream_descriptor.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070021#include <boost/asio/steady_timer.hpp>
James Feist8675a912019-10-16 14:36:58 -070022#include <boost/container/flat_set.hpp>
Rohit Chandel52639be2021-04-14 15:10:41 +053023#include <gpiod.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
26#include <sdbusplus/bus/match.hpp>
Jason M. Bills2b973a52025-07-31 11:06:04 -070027
28#include <algorithm>
29#include <bitset>
30#include <filesystem>
31#include <forward_list>
32#include <fstream>
33#include <iostream>
34#include <list>
Feist, Jamesc95cf672019-08-29 16:10:35 -070035#include <string>
James Feist45772222019-09-27 10:38:08 -070036#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070037
Jason M. Bills2b973a52025-07-31 11:06:04 -070038extern "C"
39{
Feist, Jamesc95cf672019-08-29 16:10:35 -070040#include <i2c/smbus.h>
41#include <linux/i2c-dev.h>
42}
43
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080044/****************************************************************************/
45/******************** Global Constants/Type Declarations ********************/
46/****************************************************************************/
47constexpr const char* hsbpCpldInft =
Feist, Jamesc95cf672019-08-29 16:10:35 -070048 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080049constexpr const char* hsbpConfigIntf =
50 "xyz.openbmc_project.Configuration.HSBPConfiguration";
51constexpr const char* nvmeIntf = "xyz.openbmc_project.Inventory.Item.NVMe";
James Feistdb2e0e72019-10-07 16:34:06 -070052constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
Feist, Jamesc95cf672019-08-29 16:10:35 -070053
James Feist45772222019-09-27 10:38:08 -070054constexpr size_t scanRateSeconds = 5;
55constexpr size_t maxDrives = 8; // only 1 byte alloted
56
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080057using NvmeMapping = std::vector<std::string>;
58/***************************** End of Section *******************************/
59
60/****************************************************************************/
61/**************************** Enums Definitions *****************************/
62/****************************************************************************/
63enum class AppState : uint8_t
64{
65 idle,
66 loadingHsbpConfig,
67 hsbpConfigLoaded,
68 loadingComponents,
69 componentsLoaded,
70 loadingBackplanes,
71 backplanesLoaded,
72 loadingDrives,
73 drivesLoaded
74};
75
76enum class BlinkPattern : uint8_t
77{
78 off = 0x0,
79 error = 0x2,
80 terminate = 0x3
81};
82/***************************** End of Section *******************************/
83
84/****************************************************************************/
85/************ HSBP Configuration related struct/class Definitions ***********/
86/****************************************************************************/
87struct HsbpConfig
88{
89 size_t rootBus;
90 std::vector<std::string> supportedHsbps;
91 std::unordered_map<std::string, NvmeMapping> hsbpNvmeMap;
92 std::vector<std::string> clockBufferTypes;
93 std::vector<std::string> ioExpanderTypes;
94
95 void clearConfig()
96 {
97 rootBus = -1;
98 supportedHsbps.clear();
99 hsbpNvmeMap.clear();
100 clockBufferTypes.clear();
101 ioExpanderTypes.clear();
102 }
103};
104
105class ClockBuffer
106{
107 size_t bus;
108 size_t address;
109 std::string modeOfOperation;
110 size_t outCtrlBaseAddr;
111 size_t outCtrlByteCount;
112 std::unordered_map<std::string, std::vector<std::string>> byteMap;
113 std::string name;
114 std::string type;
115
116 int file = -1;
117 bool initialized = false;
118
119 void initialize()
120 {
121 /* Execute below operation only when mode of operation is SMBus. By
122 * default the clock buffer is configured to follow OE pin output, so we
123 * need to set the output value to 0 to disable the clock outputs. If
124 * mode of operation is IO, then the IO value will determine the
125 * disable/enable of clock output */
126 if (modeOfOperation == "SMBus")
127 {
128 if (file < 0)
129 {
130 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
131 O_RDWR | O_CLOEXEC);
132 if (file < 0)
133 {
134 std::cerr << "ClockBuffer : \"" << name
135 << "\" - Unable to open bus : " << bus << "\n";
136 return;
137 }
138 }
139
140 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
141 {
142 std::cerr << "ClockBuffer : \"" << name
143 << "\" - Unable to set address to " << address
144 << "\n";
145 return;
146 }
147
148 for (uint8_t i = 0; i < outCtrlByteCount; i++)
149 {
150 std::string byteName = "Byte" + std::to_string(i);
151
152 auto byte = byteMap.find(byteName);
153 if (byte == byteMap.end())
154 {
155 std::cerr << "ClockBuffer : \"" << name
156 << "\" - Byte map error ! Unable to find "
157 << byteName << "\n";
158 return;
159 }
160
161 /* Get current value of output control register */
162 int read = i2c_smbus_read_byte_data(
163 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
164 if (read < 0)
165 {
166 std::cerr << "ClockBuffer : \"" << name
167 << "\" - Error: Unable to read data from clock "
168 "buffer register\n";
169 return;
170 }
171
172 std::bitset<8> currByte(read);
Arun Lal K M76da1312023-05-31 12:40:46 +0000173 bool writeRequired = false;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800174
175 /* Set zero only at bit position that we have a NVMe drive (i.e.
176 * ignore where byteMap is "-"). We do not want to touch other
177 * bits */
178 for (uint8_t bit = 0; bit < 8; bit++)
179 {
180 if (byte->second.at(bit) != "-")
181 {
Arun Lal K M76da1312023-05-31 12:40:46 +0000182 writeRequired = true;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800183 currByte.reset(bit);
184 }
185 }
186
Arun Lal K M76da1312023-05-31 12:40:46 +0000187 if (writeRequired)
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800188 {
Arun Lal K M76da1312023-05-31 12:40:46 +0000189 int ret = i2c_smbus_write_byte_data(
190 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
191 static_cast<uint8_t>(currByte.to_ulong()));
192
193 if (ret < 0)
194 {
195 std::cerr
196 << "ClockBuffer : \"" << name
197 << "\" - Error: Unable to write data to clock "
198 "buffer register\n";
199 return;
200 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800201 }
202 }
203 }
204 initialized = true;
205 std::cerr << "ClockBuffer : \"" << name << "\" initialized\n";
206 }
207
208 public:
209 ClockBuffer(
210 size_t busIn, size_t addressIn, std::string& modeOfOperationIn,
211 size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
212 std::unordered_map<std::string, std::vector<std::string>>& byteMapIn,
213 std::string& nameIn, std::string& typeIn) :
Jason M. Bills2b973a52025-07-31 11:06:04 -0700214 bus(busIn), address(addressIn),
215 modeOfOperation(std::move(modeOfOperationIn)),
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800216 outCtrlBaseAddr(outCtrlBaseAddrIn),
217 outCtrlByteCount(outCtrlByteCountIn), byteMap(std::move(byteMapIn)),
218 name(std::move(nameIn)), type(std::move(typeIn))
219 {
220 initialize();
221 }
222
223 bool isInitialized()
224 {
225 if (!initialized)
226 {
227 /* There was an issue with the initialization of this component. Try
228 * to invoke initialization again */
229 initialize();
230 }
231 return initialized;
232 }
233
234 std::string getName()
235 {
236 return name;
237 }
238
239 bool enableDisableClock(std::forward_list<std::string>& nvmeDrivesInserted,
240 std::forward_list<std::string>& nvmeDrivesRemoved)
241 {
242 if (modeOfOperation != "SMBus")
243 {
244 /* The clock is enabled using IO expander. No action needed from
245 * here */
246 return true;
247 }
248
249 if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
250 {
251 /* There are no drives to update */
252 return true;
253 }
254
255 for (uint8_t i = 0; i < outCtrlByteCount; i++)
256 {
257 std::string byteName = "Byte" + std::to_string(i);
258
259 auto byte = byteMap.find(byteName);
260 if (byte == byteMap.end())
261 {
262 std::cerr << "ClockBuffer : \"" << name
263 << "\" - Byte map error ! Unable to find " << byteName
264 << "\n";
265 return false;
266 }
267
268 /* Get current value of output control register */
269 int read = i2c_smbus_read_byte_data(
270 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
271 if (read < 0)
272 {
273 std::cerr << "ClockBuffer : \"" << name
274 << "\" - Error: Unable to read data from clock "
275 "buffer register\n";
276 return false;
277 }
278
279 std::bitset<8> currByte(read);
280 bool writeRequired = false;
281
282 /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
283 * reset the bit if found in nvmeDrivesRemoved */
284 for (uint8_t bit = 0; bit < 8; bit++)
285 {
286 /* The remove function returns number of elements removed from
287 * list indicating the presence of the drive and also removing
288 * it form the list */
289 if (nvmeDrivesInserted.remove(byte->second.at(bit)))
290 {
291 writeRequired = true;
292 currByte.set(bit);
293 continue;
294 }
295
296 if (nvmeDrivesRemoved.remove(byte->second.at(bit)))
297 {
298 writeRequired = true;
299 currByte.reset(bit);
300 }
301 }
302
303 if (!writeRequired)
304 {
305 /* No Write is required as there are no changes */
306 continue;
307 }
308
309 int ret = i2c_smbus_write_byte_data(
310 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
311 static_cast<uint8_t>(currByte.to_ulong()));
312 if (ret < 0)
313 {
314 std::cerr << "ClockBuffer : \"" << name
315 << "\" - Error: Unable to write data to clock "
316 "buffer register\n";
317 return false;
318 }
319 }
320
321 return true;
322 }
323
324 ~ClockBuffer()
325 {
326 if (file >= 0)
327 {
328 close(file);
329 }
330 }
331};
332
333class IoExpander
334{
335 size_t bus;
336 size_t address;
337 size_t confIORegAddr;
338 size_t outCtrlBaseAddr;
339 size_t outCtrlByteCount;
340 std::unordered_map<std::string, std::vector<std::string>> ioMap;
341 std::string name;
342 std::string type;
343
344 int file = -1;
345 bool initialized = false;
346
347 void initialize()
348 {
349 /* Initialize the IO expander Control register to configure the IO ports
350 * as outputs and set the output to low by default */
351 if (file < 0)
352 {
353 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
354 O_RDWR | O_CLOEXEC);
355 if (file < 0)
356 {
357 std::cerr << "IoExpander : " << name
358 << " - Unable to open bus : " << bus << "\n";
359 return;
360 }
361 }
362
363 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
364 {
365 std::cerr << "IoExpander : \"" << name
366 << "\" - Unable to set address to " << address << "\n";
367 return;
368 }
369
370 for (uint8_t i = 0; i < outCtrlByteCount; i++)
371 {
372 std::string ioName = "IO" + std::to_string(i);
373
374 auto io = ioMap.find(ioName);
375 if (io == ioMap.end())
376 {
377 std::cerr << "IoExpander : \"" << name
378 << "\" - IO map error ! Unable to find " << ioName
379 << "\n";
380 return;
381 }
382
383 /* Get current value of IO configuration register */
384 int read1 = i2c_smbus_read_byte_data(
385 file, static_cast<uint8_t>(confIORegAddr + i));
386 if (read1 < 0)
387 {
388 std::cerr << "IoExpander : \"" << name
389 << "\" - Error: Unable to read data from io expander "
390 "IO control register\n";
391 return;
392 }
393
394 /* Get current value of IO Ouput register */
395 int read2 = i2c_smbus_read_byte_data(
396 file, static_cast<uint8_t>(confIORegAddr + i));
397 if (read2 < 0)
398 {
399 std::cerr << "IoExpander : \"" << name
400 << "\" - Error: Unable to read data from io expander "
401 "IO output register\n";
402 return;
403 }
404
Arun Lal K M76da1312023-05-31 12:40:46 +0000405 bool writeRequired = false;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800406 std::bitset<8> currCtrlVal(read1);
407 std::bitset<8> currOutVal(read2);
408
409 /* Set zero only at bit position that we have a NVMe drive (i.e.
410 * ignore where ioMap is "-"). We do not want to touch other
411 * bits */
412 for (uint8_t bit = 0; bit < 8; bit++)
413 {
414 if (io->second.at(bit) != "-")
415 {
Arun Lal K M76da1312023-05-31 12:40:46 +0000416 writeRequired = true;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800417 currCtrlVal.reset(bit);
418 currOutVal.reset(bit);
419 }
420 }
421
Arun Lal K M76da1312023-05-31 12:40:46 +0000422 if (writeRequired)
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800423 {
Arun Lal K M76da1312023-05-31 12:40:46 +0000424 int ret1 = i2c_smbus_write_byte_data(
425 file, static_cast<uint8_t>(confIORegAddr + i),
426 static_cast<uint8_t>(currCtrlVal.to_ulong()));
427 if (ret1 < 0)
428 {
429 std::cerr
430 << "IoExpander : \"" << name
431 << "\" - Error: Unable to write data to IO expander "
432 "IO control register\n";
433 return;
434 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800435
Arun Lal K M76da1312023-05-31 12:40:46 +0000436 int ret2 = i2c_smbus_write_byte_data(
437 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
438 static_cast<uint8_t>(currOutVal.to_ulong()));
439 if (ret2 < 0)
440 {
441 std::cerr
442 << "IoExpander : \"" << name
443 << "\" - Error: Unable to write data to IO expander "
444 "IO output register\n";
445 return;
446 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800447 }
448 }
449 initialized = true;
450 std::cerr << "IoExpander : \"" << name << "\" initialized\n";
451 }
452
453 public:
454 IoExpander(
455 size_t busIn, size_t addressIn, size_t confIORegAddrIn,
456 size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
457 std::unordered_map<std::string, std::vector<std::string>>& ioMapIn,
458 std::string& nameIn, std::string& typeIn) :
Jason M. Bills2b973a52025-07-31 11:06:04 -0700459 bus(busIn), address(addressIn), confIORegAddr(confIORegAddrIn),
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800460 outCtrlBaseAddr(outCtrlBaseAddrIn),
461 outCtrlByteCount(outCtrlByteCountIn), ioMap(std::move(ioMapIn)),
462 name(std::move(nameIn)), type(std::move(typeIn))
463 {
464 initialize();
465 }
466
467 bool isInitialized()
468 {
469 if (!initialized)
470 {
471 /* There was an issue with the initialization of this component. Try
472 * to invoke initialization again */
473 initialize();
474 }
475 return initialized;
476 }
477
478 std::string getName()
479 {
480 return name;
481 }
482
483 bool enableDisableOuput(std::forward_list<std::string>& nvmeDrivesInserted,
484 std::forward_list<std::string>& nvmeDrivesRemoved)
485 {
486 if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
487 {
488 /* There are no drives to update */
489 return true;
490 }
491
492 for (uint8_t i = 0; i < outCtrlByteCount; i++)
493 {
494 std::string ioName = "IO" + std::to_string(i);
495
496 auto io = ioMap.find(ioName);
497 if (io == ioMap.end())
498 {
499 std::cerr << "IoExpander : \"" << name
500 << "\" - IO map error ! Unable to find " << ioName
501 << "\n";
502 return false;
503 }
504
505 /* Get current value of IO output register */
506 int read = i2c_smbus_read_byte_data(
507 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
508 if (read < 0)
509 {
510 std::cerr << "IoExpander : \"" << name
511 << "\" - Error: Unable to read data from io expander "
512 "register\n";
513 return false;
514 }
515
516 std::bitset<8> currVal(read);
517 bool writeRequired = false;
518
519 /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
520 * reset the bit if found in nvmeDrivesRemoved */
521 for (uint8_t bit = 0; bit < 8; bit++)
522 {
523 /* The remove function returns number of elements removed from
524 * list indicating the presence of the drive and also removing
525 * it form the list */
526 if (nvmeDrivesInserted.remove(io->second.at(bit)))
527 {
528 writeRequired = true;
529 currVal.set(bit);
530 continue;
531 }
532
533 if (nvmeDrivesRemoved.remove(io->second.at(bit)))
534 {
535 writeRequired = true;
536 currVal.reset(bit);
537 }
538 }
539
540 if (!writeRequired)
541 {
542 /* No Write is required as there are no changes */
543 continue;
544 }
545
546 int ret = i2c_smbus_write_byte_data(
547 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
548 static_cast<uint8_t>(currVal.to_ulong()));
549 if (ret < 0)
550 {
551 std::cerr << "IoExpander : \"" << name
552 << "\" - Error: Unable to write data to IO expander "
553 "register\n";
554 return false;
555 }
556 }
557
558 return true;
559 }
560
561 ~IoExpander()
562 {
563 if (file >= 0)
564 {
565 close(file);
566 }
567 }
568};
569/***************************** End of Section *******************************/
570
571/****************************************************************************/
572/*********************** Global Variables Declarations **********************/
573/****************************************************************************/
574/* State os Application */
575static AppState appState = AppState::idle;
576
577/* Configuration and Components */
578static HsbpConfig hsbpConfig;
579std::forward_list<ClockBuffer> clockBuffers;
580std::forward_list<IoExpander> ioExpanders;
581
582/* Boost IO context and Dbus variables */
Feist, Jamesc95cf672019-08-29 16:10:35 -0700583boost::asio::io_context io;
584auto conn = std::make_shared<sdbusplus::asio::connection>(io);
585sdbusplus::asio::object_server objServer(conn);
586
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800587/* GPIO Lines and GPIO Event Descriptors */
Rohit Chandel52639be2021-04-14 15:10:41 +0530588static gpiod::line nvmeLvc3AlertLine;
589static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800590/***************************** End of Section *******************************/
Rohit Chandel52639be2021-04-14 15:10:41 +0530591
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800592/****************************************************************************/
593/********** HSBP Backplane related struct and Global definitions ************/
594/****************************************************************************/
James Feist0b236ab2019-10-02 09:09:16 -0700595struct Mux
596{
James Feist8675a912019-10-16 14:36:58 -0700597 Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
598 bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
Jason M. Bills2b973a52025-07-31 11:06:04 -0700599 {}
James Feist0b236ab2019-10-02 09:09:16 -0700600 size_t bus;
601 size_t address;
James Feist8675a912019-10-16 14:36:58 -0700602 size_t channels;
603 size_t index;
604
605 // to sort in the flat set
606 bool operator<(const Mux& rhs) const
607 {
608 return index < rhs.index;
609 }
James Feist0b236ab2019-10-02 09:09:16 -0700610};
James Feist09dd2312019-10-09 09:29:03 -0700611
James Feist09dd2312019-10-09 09:29:03 -0700612struct Led : std::enable_shared_from_this<Led>
613{
614 // led pattern addresses start at 0x10
615 Led(const std::string& path, size_t index, int fd) :
616 address(static_cast<uint8_t>(index + 0x10)), file(fd),
617 ledInterface(objServer.add_interface(path, ledGroup::interface))
618 {
619 if (index >= maxDrives)
620 {
621 throw std::runtime_error("Invalid drive index");
622 }
623
624 if (!set(BlinkPattern::off))
625 {
626 std::cerr << "Cannot initialize LED " << path << "\n";
627 }
628 }
629
630 // this has to be called outside the constructor for shared_from_this to
631 // work
632 void createInterface(void)
633 {
James Feist09dd2312019-10-09 09:29:03 -0700634 ledInterface->register_property(
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530635 ledGroup::asserted, false,
636 [weakRef{weak_from_this()}](const bool req, bool& val) {
637 auto self = weakRef.lock();
638 if (!self)
639 {
640 return 0;
641 }
James Feist09dd2312019-10-09 09:29:03 -0700642 if (req == val)
643 {
644 return 1;
645 }
James Feist9f6565d2019-10-09 13:15:13 -0700646
647 if (!isPowerOn())
648 {
649 std::cerr << "Can't change blink state when power is off\n";
650 throw std::runtime_error(
651 "Can't change blink state when power is off");
652 }
James Feist09dd2312019-10-09 09:29:03 -0700653 BlinkPattern pattern =
654 req ? BlinkPattern::error : BlinkPattern::terminate;
655 if (!self->set(pattern))
656 {
James Feist9f6565d2019-10-09 13:15:13 -0700657 std::cerr << "Can't change blink pattern\n";
James Feist09dd2312019-10-09 09:29:03 -0700658 throw std::runtime_error("Cannot set blink pattern");
659 }
660 val = req;
661 return 1;
662 });
663 ledInterface->initialize();
664 }
665
666 virtual ~Led()
667 {
668 objServer.remove_interface(ledInterface);
669 }
670
671 bool set(BlinkPattern pattern)
672 {
673 int ret = i2c_smbus_write_byte_data(file, address,
674 static_cast<uint8_t>(pattern));
675 return ret >= 0;
676 }
677
678 uint8_t address;
679 int file;
680 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
681};
682
James Feist45772222019-09-27 10:38:08 -0700683struct Drive
684{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800685 Drive(std::string driveName, bool present, bool isOperational, bool nvme,
Jason M. Bills2b973a52025-07-31 11:06:04 -0700686 bool rebuilding) : isNvme(nvme), isPresent(present), name(driveName)
James Feist45772222019-09-27 10:38:08 -0700687 {
688 constexpr const char* basePath =
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800689 "/xyz/openbmc_project/inventory/item/drive/";
690 itemIface =
691 objServer.add_interface(basePath + driveName, inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700692 itemIface->register_property("Present", isPresent);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800693 itemIface->register_property("PrettyName", driveName);
James Feist45772222019-09-27 10:38:08 -0700694 itemIface->initialize();
695 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700696 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700697 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feist42b49c12019-10-29 15:18:43 -0700698
699 operationalIface->register_property(
700 "Functional", isOperational,
701 [this](const bool req, bool& property) {
702 if (!isPresent)
703 {
704 return 0;
705 }
706 if (property == req)
707 {
708 return 1;
709 }
710 property = req;
711 if (req)
712 {
713 clearFailed();
714 return 1;
715 }
716 markFailed();
717 return 1;
718 });
719
James Feist45772222019-09-27 10:38:08 -0700720 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700721 rebuildingIface = objServer.add_interface(
722 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
723 rebuildingIface->register_property("Rebuilding", rebuilding);
724 rebuildingIface->initialize();
James Feist8675a912019-10-16 14:36:58 -0700725 driveIface =
726 objServer.add_interface(itemIface->get_object_path(),
727 "xyz.openbmc_project.Inventory.Item.Drive");
728 driveIface->initialize();
James Feist42b49c12019-10-29 15:18:43 -0700729 associations = objServer.add_interface(itemIface->get_object_path(),
730 association::interface);
731 associations->register_property("Associations",
732 std::vector<Association>{});
733 associations->initialize();
734
735 if (isPresent && (!isOperational || rebuilding))
736 {
737 markFailed();
738 }
James Feist45772222019-09-27 10:38:08 -0700739 }
James Feist09dd2312019-10-09 09:29:03 -0700740 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700741 {
742 objServer.remove_interface(itemIface);
743 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700744 objServer.remove_interface(rebuildingIface);
James Feist8675a912019-10-16 14:36:58 -0700745 objServer.remove_interface(assetIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700746 objServer.remove_interface(driveIface);
James Feist42b49c12019-10-29 15:18:43 -0700747 objServer.remove_interface(associations);
James Feist0b236ab2019-10-02 09:09:16 -0700748 }
749
James Feistc66735b2020-07-17 13:51:21 -0700750 void removeAsset()
751 {
752 objServer.remove_interface(assetIface);
753 assetIface = nullptr;
754 }
755
James Feist8675a912019-10-16 14:36:58 -0700756 void createAsset(
757 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700758 {
James Feist8675a912019-10-16 14:36:58 -0700759 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700760 {
761 return;
762 }
James Feist8675a912019-10-16 14:36:58 -0700763 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700764 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700765 "xyz.openbmc_project.Inventory.Decorator.Asset");
766 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700767 {
James Feist8675a912019-10-16 14:36:58 -0700768 assetIface->register_property(key, value);
James Feistd86629c2020-04-23 10:07:25 -0700769 if (key == "SerialNumber")
770 {
771 serialNumber = value;
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700772 serialNumberInitialized = true;
James Feistd86629c2020-04-23 10:07:25 -0700773 }
James Feistdb2e0e72019-10-07 16:34:06 -0700774 }
James Feist8675a912019-10-16 14:36:58 -0700775 assetIface->initialize();
James Feistdb2e0e72019-10-07 16:34:06 -0700776 }
777
James Feist42b49c12019-10-29 15:18:43 -0700778 void markFailed(void)
779 {
780 // todo: maybe look this up via mapper
781 constexpr const char* globalInventoryPath =
782 "/xyz/openbmc_project/CallbackManager";
783
784 if (!isPresent)
785 {
786 return;
787 }
788
789 operationalIface->set_property("Functional", false);
790 std::vector<Association> warning = {
791 {"", "warning", globalInventoryPath}};
792 associations->set_property("Associations", warning);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800793 logDriveError("Drive " + name);
James Feist42b49c12019-10-29 15:18:43 -0700794 }
795
796 void clearFailed(void)
797 {
798 operationalIface->set_property("Functional", true);
799 associations->set_property("Associations", std::vector<Association>{});
800 }
801
James Feistd86629c2020-04-23 10:07:25 -0700802 void setPresent(bool set)
James Feiste8818522019-11-04 13:36:10 -0800803 {
804 // nvme drives get detected by their fru
James Feistd86629c2020-04-23 10:07:25 -0700805 if (set == isPresent)
James Feiste8818522019-11-04 13:36:10 -0800806 {
807 return;
808 }
809 itemIface->set_property("Present", set);
810 isPresent = set;
James Feistd86629c2020-04-23 10:07:25 -0700811 }
812
813 void logPresent()
814 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700815 if (isNvme && !serialNumberInitialized)
James Feiste8818522019-11-04 13:36:10 -0800816 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700817 // wait until NVMe asset is updated to include the serial number
818 // from the NVMe drive
James Feistd86629c2020-04-23 10:07:25 -0700819 return;
James Feiste8818522019-11-04 13:36:10 -0800820 }
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700821
822 if (!isPresent && loggedPresent)
823 {
824 loggedPresent = false;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800825 logDeviceRemoved("Drive", name, serialNumber);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700826 serialNumber = "N/A";
827 serialNumberInitialized = false;
James Feistc66735b2020-07-17 13:51:21 -0700828 removeAsset();
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700829 }
830 else if (isPresent && !loggedPresent)
831 {
832 loggedPresent = true;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800833 logDeviceAdded("Drive", name, serialNumber);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700834 }
James Feiste8818522019-11-04 13:36:10 -0800835 }
836
James Feist45772222019-09-27 10:38:08 -0700837 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
838 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700839 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700840 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700841 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700842 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700843
James Feist45772222019-09-27 10:38:08 -0700844 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700845 bool isPresent;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800846 std::string name;
James Feistd86629c2020-04-23 10:07:25 -0700847 std::string serialNumber = "N/A";
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700848 bool serialNumberInitialized = false;
James Feistd86629c2020-04-23 10:07:25 -0700849 bool loggedPresent = false;
James Feist45772222019-09-27 10:38:08 -0700850};
851
James Feistd86629c2020-04-23 10:07:25 -0700852struct Backplane : std::enable_shared_from_this<Backplane>
Feist, Jamesc95cf672019-08-29 16:10:35 -0700853{
James Feist45772222019-09-27 10:38:08 -0700854 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
855 const std::string& nameIn) :
Jason M. Bills2b973a52025-07-31 11:06:04 -0700856 bus(busIn), address(addressIn), backplaneIndex(backplaneIndexIn - 1),
857 name(nameIn), timer(boost::asio::steady_timer(io)),
James Feist8675a912019-10-16 14:36:58 -0700858 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Jason M. Bills2b973a52025-07-31 11:06:04 -0700859 {}
James Feistd0d36f12019-11-21 10:19:44 -0800860 void populateAsset(const std::string& rootPath, const std::string& busname)
861 {
862 conn->async_method_call(
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530863 [assetIface{assetInterface}](
James Feistd0d36f12019-11-21 10:19:44 -0800864 const boost::system::error_code ec,
865 const boost::container::flat_map<
866 std::string, std::variant<std::string>>& values) mutable {
867 if (ec)
868 {
869 std::cerr
870 << "Error getting asset tag from HSBP configuration\n";
871
872 return;
873 }
James Feistd0d36f12019-11-21 10:19:44 -0800874 for (const auto& [key, value] : values)
875 {
876 const std::string* ptr = std::get_if<std::string>(&value);
877 if (ptr == nullptr)
878 {
879 std::cerr << key << " Invalid type!\n";
880 continue;
881 }
882 assetIface->register_property(key, *ptr);
883 }
884 assetIface->initialize();
885 },
886 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
887 assetTag);
888 }
889
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800890 static std::string zeroPad(const uint8_t val)
891 {
892 std::ostringstream version;
893 version << std::setw(2) << std::setfill('0')
894 << static_cast<size_t>(val);
895 return version.str();
896 }
897
James Feistd0d36f12019-11-21 10:19:44 -0800898 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700899 {
James Feist09dd2312019-10-09 09:29:03 -0700900 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
901 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700902 if (file < 0)
903 {
904 std::cerr << "unable to open bus " << bus << "\n";
905 return;
906 }
907
908 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
909 {
910 std::cerr << "unable to set address to " << address << "\n";
911 return;
912 }
913
James Feist45772222019-09-27 10:38:08 -0700914 if (!getPresent())
915 {
916 std::cerr << "Cannot detect CPLD\n";
917 return;
918 }
919
920 getBootVer(bootVer);
921 getFPGAVer(fpgaVer);
922 getSecurityRev(securityRev);
923 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700924 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700925 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700926 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700927 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700928 hsbpItemIface->register_property("PrettyName", name);
929 hsbpItemIface->initialize();
930
James Feistd0d36f12019-11-21 10:19:44 -0800931 storageInterface = objServer.add_interface(
932 hsbpItemIface->get_object_path(),
933 "xyz.openbmc_project.Inventory.Item.StorageController");
934 storageInterface->initialize();
935
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530936 assetInterface =
937 objServer.add_interface(hsbpItemIface->get_object_path(), assetTag);
938
James Feist45772222019-09-27 10:38:08 -0700939 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800940 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700941 "xyz.openbmc_project.Software.Version");
Jason M. Bills2b973a52025-07-31 11:06:04 -0700942 versionIface->register_property(
943 "Version", zeroPad(bootVer) + "." + zeroPad(fpgaVer) + "." +
944 zeroPad(securityRev));
James Feist45772222019-09-27 10:38:08 -0700945 versionIface->register_property(
946 "Purpose",
947 std::string(
948 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
949 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000950
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530951 activationIface =
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000952 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
953 "xyz.openbmc_project.Software.Activation");
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000954 activationIface->register_property(
955 "Activation",
956 std::string(
957 "xyz.openbmc_project.Software.Activation.Activations.Active"));
958 activationIface->register_property(
959 "RequestedActivation",
960 std::string("xyz.openbmc_project.Software.Activation."
961 "RequestedActivations.None"));
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000962 activationIface->initialize();
963
James Feist45772222019-09-27 10:38:08 -0700964 getPresence(presence);
965 getIFDET(ifdet);
966
James Feistd0d36f12019-11-21 10:19:44 -0800967 populateAsset(rootPath, busname);
968
James Feist45772222019-09-27 10:38:08 -0700969 createDrives();
970
971 runTimer();
972 }
973
974 void runTimer()
975 {
James Feistd86629c2020-04-23 10:07:25 -0700976 timer.expires_after(std::chrono::seconds(scanRateSeconds));
977 timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
978 boost::system::error_code ec) {
979 auto self = weak.lock();
980 if (!self)
981 {
982 return;
983 }
James Feist45772222019-09-27 10:38:08 -0700984 if (ec == boost::asio::error::operation_aborted)
985 {
986 // we're being destroyed
987 return;
988 }
989 else if (ec)
990 {
991 std::cerr << "timer error " << ec.message() << "\n";
992 return;
993 }
James Feist9f6565d2019-10-09 13:15:13 -0700994
995 if (!isPowerOn())
996 {
997 // can't access hsbp when power is off
James Feistd86629c2020-04-23 10:07:25 -0700998 self->runTimer();
James Feist9f6565d2019-10-09 13:15:13 -0700999 return;
1000 }
James Feist45772222019-09-27 10:38:08 -07001001
James Feistd86629c2020-04-23 10:07:25 -07001002 self->getPresence(self->presence);
1003 self->getIFDET(self->ifdet);
1004 self->getFailed(self->failed);
1005 self->getRebuild(self->rebuilding);
James Feist45772222019-09-27 10:38:08 -07001006
James Feistd86629c2020-04-23 10:07:25 -07001007 self->updateDrives();
1008 self->runTimer();
James Feist45772222019-09-27 10:38:08 -07001009 });
1010 }
1011
1012 void createDrives()
1013 {
James Feist45772222019-09-27 10:38:08 -07001014 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001015 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001016 uint8_t driveSlot = (1 << ii);
1017 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1018 bool isPresent = isNvme || (presence & driveSlot);
1019 bool isFailed = !isPresent || failed & driveSlot;
1020 bool isRebuilding = !isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -07001021
1022 // +1 to convert from 0 based to 1 based
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001023 std::string driveName = boost::replace_all_copy(name, " ", "_") +
1024 "_Drive_" + std::to_string(ii + 1);
1025 Drive& drive = drives.emplace_back(driveName, isPresent, !isFailed,
James Feist09dd2312019-10-09 09:29:03 -07001026 isNvme, isRebuilding);
1027 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
1028 drive.itemIface->get_object_path(), ii, file));
1029 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001030 }
1031 }
1032
James Feist45772222019-09-27 10:38:08 -07001033 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -07001034 {
James Feist42b49c12019-10-29 15:18:43 -07001035 size_t ii = 0;
1036
1037 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001038 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001039 uint8_t driveSlot = (1 << ii);
1040 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1041 bool isPresent = isNvme || (presence & driveSlot);
1042 bool isFailed = !isPresent || (failed & driveSlot);
1043 bool isRebuilding = isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -07001044
James Feist42b49c12019-10-29 15:18:43 -07001045 it->isNvme = isNvme;
James Feistd86629c2020-04-23 10:07:25 -07001046 it->setPresent(isPresent);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001047 it->logPresent();
James Feistda0c35f2020-02-03 10:16:13 -08001048
James Feist42b49c12019-10-29 15:18:43 -07001049 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
1050 if (isFailed || isRebuilding)
1051 {
1052 it->markFailed();
1053 }
1054 else
1055 {
1056 it->clearFailed();
1057 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001058 }
James Feist45772222019-09-27 10:38:08 -07001059 }
1060
1061 bool getPresent()
1062 {
1063 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001064 return present;
1065 }
James Feist45772222019-09-27 10:38:08 -07001066
1067 bool getTypeID(uint8_t& val)
1068 {
1069 constexpr uint8_t addr = 2;
1070 int ret = i2c_smbus_read_byte_data(file, addr);
1071 if (ret < 0)
1072 {
1073 std::cerr << "Error " << __FUNCTION__ << "\n";
1074 return false;
1075 }
1076 val = static_cast<uint8_t>(ret);
1077 return true;
1078 }
1079
1080 bool getBootVer(uint8_t& val)
1081 {
1082 constexpr uint8_t addr = 3;
1083 int ret = i2c_smbus_read_byte_data(file, addr);
1084 if (ret < 0)
1085 {
1086 std::cerr << "Error " << __FUNCTION__ << "\n";
1087 return false;
1088 }
1089 val = static_cast<uint8_t>(ret);
1090 return true;
1091 }
1092
1093 bool getFPGAVer(uint8_t& val)
1094 {
1095 constexpr uint8_t addr = 4;
1096 int ret = i2c_smbus_read_byte_data(file, addr);
1097 if (ret < 0)
1098 {
1099 std::cerr << "Error " << __FUNCTION__ << "\n";
1100 return false;
1101 }
1102 val = static_cast<uint8_t>(ret);
1103 return true;
1104 }
1105
1106 bool getSecurityRev(uint8_t& val)
1107 {
1108 constexpr uint8_t addr = 5;
1109 int ret = i2c_smbus_read_byte_data(file, addr);
1110 if (ret < 0)
1111 {
1112 std::cerr << "Error " << __FUNCTION__ << "\n";
1113 return false;
1114 }
1115 val = static_cast<uint8_t>(ret);
1116 return true;
1117 }
1118
1119 bool getPresence(uint8_t& val)
1120 {
1121 // NVMe drives do not assert PRSNTn, and as such do not get reported as
1122 // PRESENT in this register
1123
1124 constexpr uint8_t addr = 8;
1125
1126 int ret = i2c_smbus_read_byte_data(file, addr);
1127 if (ret < 0)
1128 {
1129 std::cerr << "Error " << __FUNCTION__ << "\n";
1130 return false;
1131 }
1132 // presence is inverted
1133 val = static_cast<uint8_t>(~ret);
1134 return true;
1135 }
1136
1137 bool getIFDET(uint8_t& val)
1138 {
1139 // This register is a bitmap of parallel GPIO pins connected to the
1140 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
1141 // IFDETn low when they are inserted into the HSBP.This register, in
1142 // combination with the PRESENCE register, are used by the BMC to detect
1143 // the presence of NVMe drives.
1144
1145 constexpr uint8_t addr = 9;
1146
1147 int ret = i2c_smbus_read_byte_data(file, addr);
1148 if (ret < 0)
1149 {
1150 std::cerr << "Error " << __FUNCTION__ << "\n";
1151 return false;
1152 }
1153 // ifdet is inverted
1154 val = static_cast<uint8_t>(~ret);
1155 return true;
1156 }
1157
1158 bool getFailed(uint8_t& val)
1159 {
1160 constexpr uint8_t addr = 0xC;
1161 int ret = i2c_smbus_read_byte_data(file, addr);
1162 if (ret < 0)
1163 {
1164 std::cerr << "Error " << __FUNCTION__ << "\n";
1165 return false;
1166 }
1167 val = static_cast<uint8_t>(ret);
1168 return true;
1169 }
1170
1171 bool getRebuild(uint8_t& val)
1172 {
1173 constexpr uint8_t addr = 0xD;
1174 int ret = i2c_smbus_read_byte_data(file, addr);
1175 if (ret < 0)
1176 {
James Feistd86629c2020-04-23 10:07:25 -07001177 std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
1178 << "\n";
James Feist45772222019-09-27 10:38:08 -07001179 return false;
1180 }
1181 val = static_cast<uint8_t>(ret);
1182 return true;
1183 }
1184
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001185 bool getInsertedAndRemovedNvmeDrives(
1186 std::forward_list<std::string>& nvmeDrivesInserted,
1187 std::forward_list<std::string>& nvmeDrivesRemoved)
1188 {
1189 /* Get the current drives status */
1190 std::bitset<8> currDriveStatus;
1191 uint8_t nPresence;
1192 uint8_t nIfdet;
1193
1194 if (!getPresence(nPresence) || !getIFDET(nIfdet))
1195 {
1196 /* Error getting value. Return */
1197 std::cerr << "Backplane " << name
1198 << " failed to get drive status\n";
1199 return false;
1200 }
1201
1202 std::string dbusHsbpName = boost::replace_all_copy(name, " ", "_");
1203 auto nvmeMap = hsbpConfig.hsbpNvmeMap.find(dbusHsbpName);
1204 if (nvmeMap == hsbpConfig.hsbpNvmeMap.end())
1205 {
1206 std::cerr << "Couldn't get the NVMe Map for the backplane : "
1207 << name << "\n";
1208 return false;
1209 }
1210
1211 /* NVMe drives do not assert PRSNTn, and as such do not get reported in
1212 * "presence" register, but assert ifdet low. This implies for a NVMe
1213 * drive to be present, corresponding precense bit has to be 0 and idfet
1214 * has to be 1 (as the values of these regosters are negated: check
1215 * getPresence() and getIfdet() functions) */
1216 for (uint8_t bit = 0; bit < 8; bit++)
1217 {
1218 if ((nPresence & (1U << bit)) == 0)
1219 {
1220 if (nIfdet & (1U << bit))
1221 {
1222 currDriveStatus.set(bit);
1223 }
1224 }
1225 }
1226
1227 /* Determine Inserted and Removed Drives
1228 * Prev Bit | Curr Bit | Status
1229 * 0 | 0 | No Change
1230 * 0 | 1 | Inserted
1231 * 1 | 0 | Removed
1232 * 1 | 1 | No Change
1233 */
1234 for (uint8_t index = 0; index < 8; index++)
1235 {
1236 /* Inserted */
1237 if (!prevDriveStatus.test(index) && currDriveStatus.test(index))
1238 {
1239 nvmeDrivesInserted.emplace_front(nvmeMap->second.at(index));
1240 std::cerr << name << " : " << nvmeDrivesInserted.front()
1241 << " Inserted !\n";
1242 }
1243
1244 /* Removed */
1245 else if (prevDriveStatus.test(index) &&
1246 !currDriveStatus.test(index))
1247 {
1248 nvmeDrivesRemoved.emplace_front(nvmeMap->second.at(index));
1249 std::cerr << name << " : " << nvmeDrivesRemoved.front()
1250 << " Removed !\n";
1251 }
1252 }
1253
1254 prevDriveStatus = currDriveStatus;
1255 return true;
1256 }
1257
James Feist09dd2312019-10-09 09:29:03 -07001258 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -07001259 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001260 timer.cancel();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001261 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -07001262 objServer.remove_interface(versionIface);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001263 objServer.remove_interface(storageInterface);
1264 objServer.remove_interface(assetInterface);
Vikash Chandolaad11f7d2023-04-28 17:23:52 +05301265 objServer.remove_interface(activationIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001266 if (file >= 0)
1267 {
1268 close(file);
1269 }
1270 }
1271
1272 size_t bus;
1273 size_t address;
James Feist45772222019-09-27 10:38:08 -07001274 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001275 std::string name;
James Feistd86629c2020-04-23 10:07:25 -07001276 boost::asio::steady_timer timer;
James Feist45772222019-09-27 10:38:08 -07001277 bool present = false;
1278 uint8_t typeId = 0;
1279 uint8_t bootVer = 0;
1280 uint8_t fpgaVer = 0;
1281 uint8_t securityRev = 0;
1282 uint8_t funSupported = 0;
1283 uint8_t presence = 0;
1284 uint8_t ifdet = 0;
1285 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -07001286 uint8_t rebuilding = 0;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001287 std::bitset<8> prevDriveStatus;
James Feist45772222019-09-27 10:38:08 -07001288
1289 int file = -1;
1290
Feist, Jamesc95cf672019-08-29 16:10:35 -07001291 std::string type;
1292
1293 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -07001294 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -08001295 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
1296 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
Vikash Chandolaad11f7d2023-04-28 17:23:52 +05301297 std::shared_ptr<sdbusplus::asio::dbus_interface> activationIface;
James Feist42b49c12019-10-29 15:18:43 -07001298 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -07001299 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -07001300 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001301};
1302
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001303/* Global HSBP backplanes and NVMe drives collection */
James Feistd86629c2020-04-23 10:07:25 -07001304std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
James Feist42b49c12019-10-29 15:18:43 -07001305std::list<Drive> ownerlessDrives; // drives without a backplane
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001306/***************************** End of Section *******************************/
Feist, Jamesc95cf672019-08-29 16:10:35 -07001307
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001308/****************************************************************************/
1309/***************** Miscellaneous Class/Function Definitions *****************/
1310/****************************************************************************/
1311/* The purpose of this class is to sync the code flow. Often times there could
1312 * be multiple dbus calls which are async, and upon completely finishing all
1313 * Dbus calls, we need to call next function, or handle the error.
1314 * When an object of this class goes out of scope, the respective handlers
1315 * will be called */
1316class AsyncCallbackHandler
1317{
1318 bool errorOccurred = false;
1319 std::function<void()> onSuccess = nullptr;
1320 std::function<void()> onError = nullptr;
1321
1322 public:
1323 explicit AsyncCallbackHandler(std::function<void()> onSuccessIn,
1324 std::function<void()> onErrorIn) :
Jason M. Bills2b973a52025-07-31 11:06:04 -07001325 onSuccess(std::move(onSuccessIn)), onError(std::move(onErrorIn))
1326 {}
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001327
1328 void setError()
1329 {
1330 errorOccurred = true;
1331 }
1332
1333 ~AsyncCallbackHandler()
1334 {
1335 /* If error occurred flag was set, execute the error handler */
1336 if (errorOccurred)
1337 {
1338 /* Check if Error Handler is defined */
1339 if (onError)
1340 {
1341 onError();
1342 }
1343
1344 return;
1345 }
1346
1347 /* If Success Handler is present, execute Success Handler */
1348 if (onSuccess)
1349 {
1350 onSuccess();
1351 }
1352 }
1353};
1354
1355void stopHsbpManager()
1356{
1357 std::cerr << __FUNCTION__ << ": Stopping hsbp-manager\n";
1358 appState = AppState::idle;
1359 hsbpConfig.clearConfig();
1360 clockBuffers.clear();
1361 ioExpanders.clear();
1362 backplanes.clear();
1363
1364 io.stop();
1365}
1366/***************************** End of Section *******************************/
1367
1368/****************************************************************************/
1369/********* HSBP clock enable/disable related Function Definitions ***********/
1370/****************************************************************************/
1371void updateHsbpClocks(std::forward_list<std::string>& nvmeDrivesInserted,
1372 std::forward_list<std::string>& nvmeDrivesRemoved)
1373{
1374 if (appState < AppState::backplanesLoaded)
1375 {
1376 std::cerr << "HSBP not initialized ! Cancelling Clock Update ! \n";
1377 return;
1378 }
1379
1380 std::cerr << "Updating HSBP drive clocks ...\n";
1381
1382 /* Loop through all clock buffers and try to update the clocks (this will be
1383 * done if the mode of operation of the clock buffer is SMBus) */
1384 for (auto& clockBuffer : clockBuffers)
1385 {
1386 if (!clockBuffer.enableDisableClock(nvmeDrivesInserted,
1387 nvmeDrivesRemoved))
1388 {
1389 std::cerr << "Error Occurred while setting the clock in \""
1390 << clockBuffer.getName() << "\"\n";
1391 }
1392 }
1393
1394 /* If there are drives yet to be updated, check all the IO Expanders in case
1395 * they are mapped to the drives and enable the respective IO */
1396 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1397 {
1398 for (auto& ioExpander : ioExpanders)
1399 {
1400 if (!ioExpander.enableDisableOuput(nvmeDrivesInserted,
1401 nvmeDrivesRemoved))
1402 {
1403 std::cerr << "Error Occurred while setting the IO in \""
1404 << ioExpander.getName() << "\"\n";
1405 }
1406 }
1407 }
1408
1409 /* If there are drives still left, then one or more drives clock
1410 * enable/diable failed. There is a possibility of improper mapping or
1411 * current communication with the device failed */
1412 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1413 {
1414 std::cerr << "Critical Error !!!\nMapping issue detected !\n";
1415
1416 if (!nvmeDrivesInserted.empty())
1417 {
1418 std::cerr << "The clock enable failed for : ";
1419 for (auto& nvme1 : nvmeDrivesInserted)
1420 {
1421 std::cerr << nvme1 << ", ";
1422 }
1423 std::cerr << "\n";
1424 }
1425
1426 if (!nvmeDrivesRemoved.empty())
1427 {
1428 std::cerr << "The clock disable failed for : ";
1429 for (auto& nvme1 : nvmeDrivesRemoved)
1430 {
1431 std::cerr << nvme1 << ", ";
1432 }
1433 std::cerr << "\n";
1434 }
1435 }
1436}
1437
1438void scanHsbpDrives(bool& hsbpDriveScanInProgress)
1439{
1440 std::cerr << __FUNCTION__ << ": Scanning HSBP drives status ...\n";
1441
1442 /* List variables to store the drives Inserted/Removed */
1443 std::forward_list<std::string> nvmeDrivesInserted;
1444 std::forward_list<std::string> nvmeDrivesRemoved;
1445
1446 /* Loop through each backplane present and get the list of inserted/removed
1447 * drives */
1448 for (auto& [name, backplane] : backplanes)
1449 {
1450 backplane->getInsertedAndRemovedNvmeDrives(nvmeDrivesInserted,
1451 nvmeDrivesRemoved);
1452 }
1453
1454 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1455 {
1456 updateHsbpClocks(nvmeDrivesInserted, nvmeDrivesRemoved);
1457 }
1458
1459 std::cerr << __FUNCTION__ << ": Scanning HSBP drives Completed\n";
1460
1461 hsbpDriveScanInProgress = false;
1462}
1463
1464void checkHsbpDrivesStatus()
1465{
1466 static bool hsbpDriveScanInProgress = false;
1467 static bool hsbpDriveRescanInQueue = false;
1468
1469 if (appState < AppState::backplanesLoaded)
1470 {
1471 std::cerr << __FUNCTION__
1472 << ": HSBP not initialized ! Cancelling scan of HSBP drives "
1473 "status ! \n";
1474 return;
1475 }
1476
1477 if (hsbpDriveScanInProgress)
1478 {
1479 /* Scan and Clock Update already in progress. Try again after sometime.
1480 * This event can occur due to the GPIO interrupt */
1481 std::cerr << __FUNCTION__
1482 << ": HSBP Drives Scan is already in progress\n";
1483 if (hsbpDriveRescanInQueue)
1484 {
1485 /* There is already a Re-Scan in queue. No need to create multiple
1486 * rescans */
1487 return;
1488 }
1489
1490 hsbpDriveRescanInQueue = true;
1491
1492 std::cerr << __FUNCTION__ << ": Queuing the Scan \n";
1493
1494 auto driveScanTimer = std::make_shared<boost::asio::steady_timer>(io);
1495 driveScanTimer->expires_after(std::chrono::seconds(1));
1496 driveScanTimer->async_wait(
1497 [driveScanTimer](const boost::system::error_code ec) {
1498 if (ec == boost::asio::error::operation_aborted)
1499 {
1500 // Timer was Aborted
1501 return;
1502 }
1503 else if (ec)
1504 {
1505 std::cerr << "driveScanTimer: Timer error" << ec.message()
1506 << "\n";
1507 return;
1508 }
1509 hsbpDriveRescanInQueue = false;
1510 checkHsbpDrivesStatus();
1511 });
1512
1513 return;
1514 }
1515
1516 hsbpDriveScanInProgress = true;
1517
1518 /* Post the scan to IO queue and return from here. This enables capturing
1519 * next GPIO event if any */
1520 boost::asio::post(io, []() { scanHsbpDrives(hsbpDriveScanInProgress); });
1521}
1522/***************************** End of Section *******************************/
1523
1524/****************************************************************************/
1525/********** Backplanes and NVMe drives related Function Definitions *********/
1526/****************************************************************************/
James Feist8675a912019-10-16 14:36:58 -07001527static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -07001528{
James Feist8675a912019-10-16 14:36:58 -07001529 size_t count = 0;
1530 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -07001531 {
James Feistd86629c2020-04-23 10:07:25 -07001532 count += backplane->drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -07001533 }
James Feist8675a912019-10-16 14:36:58 -07001534 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -07001535}
1536
James Feist8675a912019-10-16 14:36:58 -07001537void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -07001538{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001539 appState = AppState::loadingDrives;
1540
1541 /* Setup a callback to be called once the assets are populated completely or
1542 * fallback to error handler */
1543 auto drivesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1544 []() {
1545 appState = AppState::drivesLoaded;
1546 std::cerr << "Drives Updated !\n";
1547 },
1548 []() {
1549 // TODO: Handle this error if needed
1550 appState = AppState::backplanesLoaded;
1551 std::cerr << "An error occured ! Drives load failed \n";
1552 });
James Feist0b236ab2019-10-02 09:09:16 -07001553
1554 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001555 [drivesLoadedCallback](const boost::system::error_code ec,
1556 const GetSubTreeType& subtree) {
James Feist0b236ab2019-10-02 09:09:16 -07001557 if (ec)
1558 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001559 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1560 << ec.message() << "\n";
1561 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001562 return;
1563 }
James Feist8675a912019-10-16 14:36:58 -07001564
1565 // drives may get an owner during this, or we might disover more
1566 // drives
1567 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -07001568 for (const auto& [path, objDict] : subtree)
1569 {
1570 if (objDict.empty())
1571 {
1572 continue;
1573 }
1574
1575 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -07001576 // we export this interface too
1577 if (owner == busName)
1578 {
1579 continue;
1580 }
James Feist8675a912019-10-16 14:36:58 -07001581 if (std::find(objDict.begin()->second.begin(),
Jason M. Bills2b973a52025-07-31 11:06:04 -07001582 objDict.begin()->second.end(), assetTag) ==
1583 objDict.begin()->second.end())
James Feist8675a912019-10-16 14:36:58 -07001584 {
1585 // no asset tag to associate to
1586 continue;
1587 }
1588
James Feist0b236ab2019-10-02 09:09:16 -07001589 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001590 [path, drivesLoadedCallback](
1591 const boost::system::error_code ec2,
1592 const boost::container::flat_map<
1593 std::string, std::variant<uint64_t, std::string>>&
1594 values) {
James Feist0b236ab2019-10-02 09:09:16 -07001595 if (ec2)
1596 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001597 std::cerr
1598 << __FUNCTION__ << ": Error Getting Config "
1599 << ec2.message() << " "
1600 << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001601 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001602 return;
1603 }
1604 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -07001605
James Feist8675a912019-10-16 14:36:58 -07001606 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -07001607 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001608 std::cerr
1609 << __FUNCTION__ << ": Illegal interface at "
1610 << path << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001611 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001612 return;
1613 }
1614
James Feist8675a912019-10-16 14:36:58 -07001615 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -07001616 size_t muxBus = static_cast<size_t>(
1617 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -07001618 std::filesystem::path muxPath =
1619 "/sys/bus/i2c/devices/i2c-" +
1620 std::to_string(muxBus) + "/mux_device";
1621 if (!std::filesystem::is_symlink(muxPath))
1622 {
1623 std::cerr << path << " mux does not exist\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001624 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001625 return;
1626 }
1627
James Feist8675a912019-10-16 14:36:58 -07001628 // we should be getting something of the form 7-0052
1629 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -07001630 std::string fname =
1631 std::filesystem::read_symlink(muxPath).filename();
1632 auto findDash = fname.find('-');
1633
1634 if (findDash == std::string::npos ||
1635 findDash + 1 >= fname.size())
1636 {
1637 std::cerr << path << " mux path invalid\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001638 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001639 return;
1640 }
1641
1642 std::string busStr = fname.substr(0, findDash);
1643 std::string muxStr = fname.substr(findDash + 1);
1644
1645 size_t bus = static_cast<size_t>(std::stoi(busStr));
1646 size_t addr =
1647 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -07001648 size_t muxIndex = 0;
1649
1650 // find the channel of the mux the drive is on
Jason M. Bills2b973a52025-07-31 11:06:04 -07001651 std::ifstream nameFile(
1652 "/sys/bus/i2c/devices/i2c-" +
1653 std::to_string(muxBus) + "/name");
James Feist8675a912019-10-16 14:36:58 -07001654 if (!nameFile)
1655 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001656 std::cerr << __FUNCTION__
1657 << ": Unable to open name file of bus "
James Feist8675a912019-10-16 14:36:58 -07001658 << muxBus << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001659 drivesLoadedCallback->setError();
James Feist8675a912019-10-16 14:36:58 -07001660 return;
1661 }
1662
1663 std::string nameStr;
1664 std::getline(nameFile, nameStr);
1665
1666 // file is of the form "i2c-4-mux (chan_id 1)", get chan
1667 // assume single digit chan
1668 const std::string prefix = "chan_id ";
1669 size_t findId = nameStr.find(prefix);
1670 if (findId == std::string::npos ||
1671 findId + 1 >= nameStr.size())
1672 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001673 std::cerr
1674 << __FUNCTION__ << ": Illegal name file on bus "
1675 << muxBus << "\n";
James Feist8675a912019-10-16 14:36:58 -07001676 }
1677
1678 std::string indexStr =
1679 nameStr.substr(findId + prefix.size(), 1);
1680
1681 size_t driveIndex = std::stoi(indexStr);
1682
James Feist8675a912019-10-16 14:36:58 -07001683 boost::container::flat_map<std::string, std::string>
1684 assetInventory;
1685 const std::array<const char*, 4> assetKeys = {
1686 "PartNumber", "SerialNumber", "Manufacturer",
1687 "Model"};
1688 for (const auto& [key, value] : values)
1689 {
1690 if (std::find(assetKeys.begin(), assetKeys.end(),
1691 key) == assetKeys.end())
1692 {
1693 continue;
1694 }
Jayaprakash Mutyala9cefb822025-05-09 17:51:18 +00001695 if (std::holds_alternative<std::string>(value))
1696 {
1697 assetInventory[key] =
1698 std::get<std::string>(value);
1699 }
James Feist8675a912019-10-16 14:36:58 -07001700 }
1701
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001702 Backplane* parent = nullptr;
1703 for (auto& [name, backplane] : backplanes)
1704 {
1705 muxIndex = 0;
1706 for (const Mux& mux : *(backplane->muxes))
1707 {
1708 if (bus == mux.bus && addr == mux.address)
1709 {
1710 parent = backplane.get();
1711 break;
1712 }
1713 muxIndex += mux.channels;
1714 }
1715 if (parent)
1716 {
1717 /* Found the backplane. No need to proceed
1718 * further */
1719 break;
1720 }
1721 }
1722
James Feist8675a912019-10-16 14:36:58 -07001723 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -07001724 if (parent == nullptr)
1725 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001726 std::string driveName =
1727 "Drive_" + std::to_string(getDriveCount() + 1);
James Feist8675a912019-10-16 14:36:58 -07001728 auto& drive = ownerlessDrives.emplace_back(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001729 driveName, true, true, true, false);
James Feist8675a912019-10-16 14:36:58 -07001730 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -07001731 return;
1732 }
James Feist8675a912019-10-16 14:36:58 -07001733
1734 driveIndex += muxIndex;
1735
James Feist0b236ab2019-10-02 09:09:16 -07001736 if (parent->drives.size() <= driveIndex)
1737 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001738 std::cerr
1739 << __FUNCTION__ << ": Illegal drive index at "
1740 << path << " " << driveIndex << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001741 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001742 return;
1743 }
James Feist42b49c12019-10-29 15:18:43 -07001744 auto it = parent->drives.begin();
1745 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -07001746
James Feist42b49c12019-10-29 15:18:43 -07001747 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -07001748 },
1749 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -07001750 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -07001751 }
1752 },
1753 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001754 0, std::array<const char*, 1>{nvmeIntf});
James Feist0b236ab2019-10-02 09:09:16 -07001755}
1756
James Feist8675a912019-10-16 14:36:58 -07001757void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -07001758 std::string& rootPath)
1759{
1760 const static std::array<const std::string, 4> muxTypes = {
1761 "xyz.openbmc_project.Configuration.PCA9543Mux",
1762 "xyz.openbmc_project.Configuration.PCA9544Mux",
1763 "xyz.openbmc_project.Configuration.PCA9545Mux",
1764 "xyz.openbmc_project.Configuration.PCA9546Mux"};
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001765
James Feist0b236ab2019-10-02 09:09:16 -07001766 conn->async_method_call(
1767 [muxes](const boost::system::error_code ec,
1768 const GetSubTreeType& subtree) {
1769 if (ec)
1770 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001771 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1772 << ec.message() << "\n";
James Feist0b236ab2019-10-02 09:09:16 -07001773 return;
1774 }
James Feist8675a912019-10-16 14:36:58 -07001775 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -07001776 for (const auto& [path, objDict] : subtree)
1777 {
1778 if (objDict.empty() || objDict.begin()->second.empty())
1779 {
1780 continue;
1781 }
1782
1783 const std::string& owner = objDict.begin()->first;
1784 const std::vector<std::string>& interfaces =
1785 objDict.begin()->second;
1786
1787 const std::string* interface = nullptr;
1788 for (const std::string& iface : interfaces)
1789 {
1790 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
1791 muxTypes.end())
1792 {
1793 interface = &iface;
1794 break;
1795 }
1796 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001797
James Feist0b236ab2019-10-02 09:09:16 -07001798 if (interface == nullptr)
1799 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001800 std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
James Feist0b236ab2019-10-02 09:09:16 -07001801 continue;
1802 }
1803
1804 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001805 [path, muxes, index](
James Feist0b236ab2019-10-02 09:09:16 -07001806 const boost::system::error_code ec2,
1807 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -07001808 std::string,
1809 std::variant<uint64_t, std::vector<std::string>>>&
1810 values) {
James Feist0b236ab2019-10-02 09:09:16 -07001811 if (ec2)
1812 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001813 std::cerr
1814 << __FUNCTION__ << ": Error Getting Config "
1815 << ec2.message() << "\n";
James Feist0b236ab2019-10-02 09:09:16 -07001816 return;
1817 }
1818 auto findBus = values.find("Bus");
1819 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -07001820 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -07001821 if (findBus == values.end() ||
1822 findAddress == values.end())
1823 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001824 std::cerr
1825 << __FUNCTION__ << ": Illegal configuration at "
1826 << path << "\n";
James Feist0b236ab2019-10-02 09:09:16 -07001827 return;
1828 }
1829 size_t bus = static_cast<size_t>(
1830 std::get<uint64_t>(findBus->second));
1831 size_t address = static_cast<size_t>(
1832 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -07001833 std::vector<std::string> channels =
1834 std::get<std::vector<std::string>>(
1835 findChannelNames->second);
1836 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -07001837 },
1838 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1839 *interface);
James Feist8675a912019-10-16 14:36:58 -07001840 index++;
James Feist0b236ab2019-10-02 09:09:16 -07001841 }
1842 },
1843 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
1844 rootPath, 1, muxTypes);
1845}
1846
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001847void populateHsbpBackplanes(
1848 const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001849{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001850 std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
1851 appState = AppState::loadingBackplanes;
Jayaprakash Mutyala0c5059f2021-11-10 22:09:55 +00001852 backplanes.clear();
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001853
Feist, Jamesc95cf672019-08-29 16:10:35 -07001854 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001855 [backplanesLoadedCallback](const boost::system::error_code ec,
1856 const GetSubTreeType& subtree) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001857 if (ec)
1858 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001859 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1860 << ec.message() << "\n";
1861 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001862 return;
1863 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001864
1865 if (subtree.empty())
1866 {
1867 /* There wer no HSBP's detected. set teh state back to
1868 * componentsLoaded so that on backplane match event, the
1869 * process can start again */
1870 appState = AppState::componentsLoaded;
1871 std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
1872 return;
1873 }
1874
Feist, Jamesc95cf672019-08-29 16:10:35 -07001875 for (const auto& [path, objDict] : subtree)
1876 {
1877 if (objDict.empty())
1878 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001879 std::cerr << __FUNCTION__
1880 << ": Subtree data "
1881 "corrupted !\n";
1882 backplanesLoadedCallback->setError();
1883 return;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001884 }
1885
1886 const std::string& owner = objDict.begin()->first;
1887 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001888 [backplanesLoadedCallback, path,
1889 owner](const boost::system::error_code ec2,
1890 const boost::container::flat_map<
1891 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001892 if (ec2)
1893 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001894 std::cerr
1895 << __FUNCTION__ << ": Error Getting Config "
1896 << ec2.message() << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001897 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001898 return;
1899 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001900 std::optional<size_t> bus;
1901 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -07001902 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001903 std::optional<std::string> name;
1904 for (const auto& [key, value] : resp)
1905 {
1906 if (key == "Bus")
1907 {
1908 bus = std::get<uint64_t>(value);
1909 }
1910 else if (key == "Address")
1911 {
1912 address = std::get<uint64_t>(value);
1913 }
James Feist45772222019-09-27 10:38:08 -07001914 else if (key == "Index")
1915 {
1916 backplaneIndex = std::get<uint64_t>(value);
1917 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001918 else if (key == "Name")
1919 {
1920 name = std::get<std::string>(value);
1921 }
1922 }
James Feist45772222019-09-27 10:38:08 -07001923 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001924 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07001925 std::cerr
1926 << __FUNCTION__ << ": Illegal configuration at "
1927 << path << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001928 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001929 return;
1930 }
James Feist0b236ab2019-10-02 09:09:16 -07001931 std::string parentPath =
1932 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001933 const auto& [backplane, status] = backplanes.emplace(
James Feistd86629c2020-04-23 10:07:25 -07001934 *name, std::make_shared<Backplane>(
1935 *bus, *address, *backplaneIndex, *name));
1936 backplane->second->run(parentPath, owner);
1937 populateMuxes(backplane->second->muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001938 },
1939 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001940 hsbpCpldInft);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001941 }
1942 },
1943 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001944 0, std::array<const char*, 1>{hsbpCpldInft});
1945}
1946
1947void setUpBackplanesAndDrives()
1948{
1949 static bool backplanesScanInProgress = false;
1950 static bool backplanesRescanInQueue = false;
1951
1952 if (appState < AppState::componentsLoaded)
1953 {
1954 std::cerr << __FUNCTION__
1955 << ": Components are not initialized ! Cancelling scan of "
1956 "Backplanes ! \n";
1957 return;
1958 }
1959
1960 if (backplanesScanInProgress)
1961 {
1962 std::cerr << __FUNCTION__
1963 << ": Backplanes Scan is already in progress\n";
1964 if (backplanesRescanInQueue)
1965 {
1966 /* There is already a Re-Scan in queue. No need to create multiple
1967 * rescans */
1968 return;
1969 }
1970
1971 backplanesRescanInQueue = true;
1972
1973 std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
1974
1975 auto backplaneScanTimer =
1976 std::make_shared<boost::asio::steady_timer>(io);
1977 backplaneScanTimer->expires_after(std::chrono::seconds(1));
1978 backplaneScanTimer->async_wait(
1979 [backplaneScanTimer](const boost::system::error_code ec) {
1980 if (ec == boost::asio::error::operation_aborted)
1981 {
1982 // Timer was Aborted
1983 return;
1984 }
1985 else if (ec)
1986 {
1987 std::cerr << "backplaneScanTimer: Timer error"
1988 << ec.message() << "\n";
1989 return;
1990 }
1991 backplanesRescanInQueue = false;
1992 setUpBackplanesAndDrives();
1993 });
1994
1995 return;
1996 }
1997
1998 backplanesScanInProgress = true;
1999
2000 /* Set Callback to be called once backplanes are populated to call
2001 * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
2002 auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
2003 []() {
2004 /* If no HSBP's were detected, the state changes to
2005 * componentsLoaded. Proceed further only if state was
2006 * loadingBackplanes */
2007 if (appState != AppState::loadingBackplanes)
2008 {
2009 backplanesScanInProgress = false;
2010 return;
2011 }
2012
2013 /* If there is a ReScan in the Queue, dont proceed further. Load the
2014 * Backplanes again and then proceed further */
2015 if (backplanesRescanInQueue)
2016 {
2017 backplanesScanInProgress = false;
2018 return;
2019 }
2020
2021 appState = AppState::backplanesLoaded;
2022 std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
2023
2024 checkHsbpDrivesStatus();
2025 updateAssets();
2026 backplanesScanInProgress = false;
2027 },
2028 []() {
2029 /* Loading Backplanes is an important step. If the load failed due
2030 * to an error, stop the app so that restart cant be triggerred */
2031 std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
2032 appState = AppState::idle;
2033 backplanesScanInProgress = false;
2034 stopHsbpManager();
2035 });
2036
2037 populateHsbpBackplanes(backplanesLoadedCallback);
2038}
2039
2040void setupBackplanesAndDrivesMatch()
2041{
2042 static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
2043 *conn,
2044 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2045 "member='PropertiesChanged', "
2046 "interface='org.freedesktop.DBus.Properties', "
2047 "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2048 std::string(hsbpCpldInft) + "'",
2049 [](sdbusplus::message_t& msg) {
2050 std::string intfName;
2051 boost::container::flat_map<std::string, BasicVariantType> values;
2052 msg.read(intfName, values);
2053
2054 /* This match will be triggered for each of the property being set
2055 * under the hsbpCpldInft interface. Call the loader only on one
2056 * property say "name". This will avoid multiple calls to populate
2057 * function
2058 */
2059 for (const auto& [key, value] : values)
2060 {
2061 if (key == "Name")
2062 {
2063 /* This match will be triggered when ever there is a
2064 * addition/removal of HSBP backplane. At this stage, all
2065 * the HSBP's need to be populated again and also assets
2066 * have to be re-discovered. So, setting state to
2067 * componentsLoaded and calling setUpBackplanesAndDrives()
2068 * only if configuration and components loading was
2069 * completed */
2070 if (appState < AppState::componentsLoaded)
2071 {
2072 /* Configuration is not loaded yet. Backplanes will be
2073 * loaded
2074 * once configuration and components are loaded. */
Jason M. Bills2b973a52025-07-31 11:06:04 -07002075 std::cerr
2076 << __FUNCTION__ << ": Discarding Backplane match\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002077 return;
2078 }
2079
2080 appState = AppState::componentsLoaded;
2081
2082 /* We will call the function after a small delay to let all
2083 * the properties to be intialized */
2084 auto backplaneTimer =
2085 std::make_shared<boost::asio::steady_timer>(io);
2086 backplaneTimer->expires_after(std::chrono::seconds(2));
2087 backplaneTimer->async_wait(
2088 [backplaneTimer](const boost::system::error_code ec) {
2089 if (ec == boost::asio::error::operation_aborted)
2090 {
2091 return;
2092 }
2093 else if (ec)
2094 {
2095 std::cerr << "backplaneTimer: Timer error"
2096 << ec.message() << "\n";
2097 return;
2098 }
2099 setUpBackplanesAndDrives();
2100 });
2101 }
2102 }
2103 });
2104
2105 static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
2106 *conn,
2107 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2108 "member='PropertiesChanged', "
2109 "interface='org.freedesktop.DBus.Properties', arg0='" +
2110 std::string(nvmeIntf) + "'",
2111 [](sdbusplus::message_t& msg) {
2112 std::string intfName;
2113 boost::container::flat_map<std::string, BasicVariantType> values;
2114 msg.read(intfName, values);
2115
2116 /* This match will be triggered for each of the property being set
2117 * under the nvmeIntf interface. Call the loader only on one
2118 * property say "name". This will avoid multiple calls to populate
2119 * function
2120 */
2121 for (const auto& [key, value] : values)
2122 {
2123 if (key == "Name")
2124 {
2125 /* This match will be triggered when ever there is a
2126 * addition/removal of drives. At this stage only assets
2127 * have to be re-discovered. So, setting state to
2128 * backplanesLoaded and calling updateAssets() only if all
2129 * previous states are completed */
2130 if (appState < AppState::backplanesLoaded)
2131 {
2132 /* Configuration is not loaded yet. Drives will be
2133 * loaded once
2134 * configuration, components and backplanes are loaded.
2135 */
Jason M. Bills2b973a52025-07-31 11:06:04 -07002136 std::cerr
2137 << __FUNCTION__ << ": Discarding Drive match\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002138 return;
2139 }
2140
2141 appState = AppState::backplanesLoaded;
2142
2143 /* We will call the function after a small delay to let all
2144 * the properties to be intialized */
2145 auto driveTimer =
2146 std::make_shared<boost::asio::steady_timer>(io);
2147 driveTimer->expires_after(std::chrono::seconds(2));
2148 driveTimer->async_wait(
2149 [driveTimer](const boost::system::error_code ec) {
2150 if (ec == boost::asio::error::operation_aborted)
2151 {
2152 return;
2153 }
2154 else if (ec)
2155 {
2156 std::cerr << "driveTimer: Timer error"
2157 << ec.message() << "\n";
2158 return;
2159 }
2160 updateAssets();
2161 });
2162 }
2163 }
2164 });
2165}
2166/***************************** End of Section *******************************/
2167
2168/****************************************************************************/
2169/******************* Components related Function Definitions ****************/
2170/****************************************************************************/
2171bool verifyComponentsLoaded()
2172{
2173 std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
2174
2175 /* Loop through all clock buffers */
2176 for (auto& clockBuffer : clockBuffers)
2177 {
2178 if (!clockBuffer.isInitialized())
2179 {
2180 std::cerr << "Critical Error: Initializing \""
2181 << clockBuffer.getName() << "\" failed\n";
2182 return false;
2183 }
2184 }
2185
2186 /* Loop through all IO Expanders */
2187 for (auto& ioExpander : ioExpanders)
2188 {
2189 if (!ioExpander.isInitialized())
2190 {
2191 std::cerr << "Critical Error: Initializing \""
2192 << ioExpander.getName() << "\" failed\n";
2193 return false;
2194 }
2195 }
2196
2197 std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
2198
2199 return true;
2200}
2201/***************************** End of Section *******************************/
2202
2203/****************************************************************************/
2204/****************** IO expander related Function Definitions ****************/
2205/****************************************************************************/
2206void loadIoExpanderInfo(
2207 const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2208{
2209 appState = AppState::loadingComponents;
2210
2211 /* Clear global ioExpanders to start off */
2212 ioExpanders.clear();
2213
2214 conn->async_method_call(
2215 [componentsLoadedCallback](const boost::system::error_code ec,
2216 const GetSubTreeType& subtree) {
2217 if (ec)
2218 {
2219 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2220 << ec.message() << "\n";
2221 componentsLoadedCallback->setError();
2222 return;
2223 }
2224
2225 for (auto& [path, objDict] : subtree)
2226 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002227 if (objDict.empty())
2228 {
2229 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2230 componentsLoadedCallback->setError();
2231 return;
2232 }
2233
2234 /* Ideally there would be only one element in objDict as only
2235 * one service exposes it and there would be only one interface
2236 * so it is safe to directly read them without loop */
2237 const std::string& service = objDict.begin()->first;
2238 const std::string& intf = objDict.begin()->second.front();
2239
2240 conn->async_method_call(
2241 [componentsLoadedCallback](
2242 const boost::system::error_code er,
2243 const boost::container::flat_map<
2244 std::string, BasicVariantType>& resp) {
2245 if (er)
2246 {
2247 std::cerr << __FUNCTION__
2248 << ": Error Getting "
2249 "Config "
2250 << er.message() << "\n";
2251 componentsLoadedCallback->setError();
2252 return;
2253 }
2254
2255 std::optional<uint64_t> bus;
2256 std::optional<uint64_t> address;
2257 std::optional<uint64_t> confIORegAddr;
2258 std::optional<uint64_t> outCtrlBaseAddr;
2259 std::optional<uint64_t> outCtrlByteCount;
2260 std::unordered_map<std::string,
2261 std::vector<std::string>>
2262 ioMap;
2263 std::optional<std::string> name;
2264 std::optional<std::string> type;
2265
2266 /* Loop through to get all IO Expander properties */
2267 for (const auto& [key, value] : resp)
2268 {
2269 if (key == "Bus")
2270 {
2271 bus = std::get<uint64_t>(value);
2272 }
2273 else if (key == "Address")
2274 {
2275 address = std::get<uint64_t>(value);
2276 }
2277 else if (key == "ConfIORegAddr")
2278 {
2279 confIORegAddr = std::get<uint64_t>(value);
2280 }
2281 else if (key == "OutCtrlBaseAddr")
2282 {
2283 outCtrlBaseAddr = std::get<uint64_t>(value);
2284 }
2285 else if (key == "OutCtrlByteCount")
2286 {
2287 outCtrlByteCount = std::get<uint64_t>(value);
2288 }
2289 else if (key == "Name")
2290 {
2291 name = std::get<std::string>(value);
2292 }
2293 else if (key == "Type")
2294 {
2295 type = std::get<std::string>(value);
2296 }
2297 else if (key.starts_with("IO"))
2298 {
2299 std::optional<std::vector<std::string>> outList;
2300 outList = std::get<NvmeMapping>(value);
2301 if (!outList)
2302 {
2303 break;
2304 }
2305 ioMap.try_emplace(key, *outList);
2306 }
2307 }
2308
2309 /* Verify if all properties were defined */
2310 if (!bus || !address || !confIORegAddr ||
2311 !outCtrlBaseAddr || !outCtrlByteCount || !name)
2312 {
2313 std::cerr << __FUNCTION__
2314 << ": Incomplete "
2315 "Clock Buffer definition !! \n";
2316 componentsLoadedCallback->setError();
2317 return;
2318 }
2319
2320 /* Check if we were able to get byteMap correctly */
2321 if ((*outCtrlByteCount) != ioMap.size())
2322 {
2323 std::cerr << "loadIoExpanderInfo(): Incomplete "
2324 "IO Map !! \n";
2325 componentsLoadedCallback->setError();
2326 return;
2327 }
2328
2329 /* Create IO expander object and add it to global
2330 * ioExpanders vector */
2331 ioExpanders.emplace_front(
2332 *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
2333 *outCtrlByteCount, ioMap, *name, *type);
2334 },
2335 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2336 intf);
2337 }
2338 },
2339 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2340 0, hsbpConfig.ioExpanderTypes);
2341}
2342/***************************** End of Section *******************************/
2343
2344/****************************************************************************/
2345/***************** Clock buffer related Function Definitions ****************/
2346/****************************************************************************/
2347void loadClockBufferInfo(
2348 const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2349{
2350 appState = AppState::loadingComponents;
2351
2352 /* Clear global clockBuffers to start off */
2353 clockBuffers.clear();
2354
2355 conn->async_method_call(
2356 [componentsLoadedCallback](const boost::system::error_code ec,
2357 const GetSubTreeType& subtree) {
2358 if (ec)
2359 {
2360 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2361 << ec.message() << "\n";
2362 componentsLoadedCallback->setError();
2363 return;
2364 }
2365
2366 for (auto& [path, objDict] : subtree)
2367 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002368 if (objDict.empty())
2369 {
2370 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2371 componentsLoadedCallback->setError();
2372 return;
2373 }
2374
2375 /* Ideally there would be only one element in objDict as only
2376 * one service exposes it and there would be only one interface
2377 * so it is safe to directly read them without loop */
2378 const std::string& service = objDict.begin()->first;
2379 const std::string& intf = objDict.begin()->second.front();
2380
2381 conn->async_method_call(
2382 [componentsLoadedCallback](
2383 const boost::system::error_code er,
2384 const boost::container::flat_map<
2385 std::string, BasicVariantType>& resp) {
2386 if (er)
2387 {
2388 std::cerr << __FUNCTION__
2389 << ": Error Getting "
2390 "Config "
2391 << er.message() << "\n";
2392 componentsLoadedCallback->setError();
2393 return;
2394 }
2395
2396 std::optional<uint64_t> bus;
2397 std::optional<uint64_t> address;
2398 std::optional<std::string> mode;
2399 std::optional<uint64_t> outCtrlBaseAddr;
2400 std::optional<uint64_t> outCtrlByteCount;
2401 std::unordered_map<std::string,
2402 std::vector<std::string>>
2403 byteMap;
2404 std::optional<std::string> name;
2405 std::optional<std::string> type;
2406
2407 /* Loop through to get all Clock Buffer properties */
2408 for (const auto& [key, value] : resp)
2409 {
2410 if (key == "Bus")
2411 {
2412 bus = std::get<uint64_t>(value);
2413 }
2414 else if (key == "Address")
2415 {
2416 address = std::get<uint64_t>(value);
2417 }
2418 else if (key == "Mode")
2419 {
2420 mode = std::get<std::string>(value);
2421 }
2422 else if (key == "OutCtrlBaseAddr")
2423 {
2424 outCtrlBaseAddr = std::get<uint64_t>(value);
2425 }
2426 else if (key == "OutCtrlByteCount")
2427 {
2428 outCtrlByteCount = std::get<uint64_t>(value);
2429 }
2430 else if (key == "Name")
2431 {
2432 name = std::get<std::string>(value);
2433 }
2434 else if (key == "Type")
2435 {
2436 type = std::get<std::string>(value);
2437 }
2438 else if (key.starts_with("Byte"))
2439 {
2440 std::optional<std::vector<std::string>>
2441 byteList;
2442 byteList = std::get<NvmeMapping>(value);
2443 if (!byteList)
2444 {
2445 break;
2446 }
2447 byteMap.try_emplace(key, *byteList);
2448 }
2449 }
2450
2451 /* Verify if all properties were defined */
2452 if (!bus || !address || !mode || !outCtrlBaseAddr ||
2453 !outCtrlByteCount || !name)
2454 {
2455 std::cerr << __FUNCTION__
2456 << ": Incomplete "
2457 "Clock Buffer definition !! \n";
2458 componentsLoadedCallback->setError();
2459 return;
2460 }
2461
2462 /* Check if we were able to get byteMap correctly */
2463 if ((*outCtrlByteCount) != byteMap.size())
2464 {
2465 std::cerr << __FUNCTION__
2466 << ": Incomplete "
2467 "Byte Map !! \n";
2468 componentsLoadedCallback->setError();
2469 return;
2470 }
2471
2472 /* Create clock buffer object and add it to global
2473 * clockBuffers vector */
2474 clockBuffers.emplace_front(
2475 *bus, *address, *mode, *outCtrlBaseAddr,
2476 *outCtrlByteCount, byteMap, *name, *type);
2477 },
2478 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2479 intf);
2480 }
2481 },
2482 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2483 0, hsbpConfig.clockBufferTypes);
2484}
2485/***************************** End of Section *******************************/
2486
2487/****************************************************************************/
2488/***************** HSBP Config related Function Definitions *****************/
2489/****************************************************************************/
2490void loadHsbpConfig()
2491{
2492 appState = AppState::loadingHsbpConfig;
2493
2494 conn->async_method_call(
2495 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
2496 if (ec)
2497 {
2498 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2499 << ec.message() << "\n";
2500 return;
2501 }
2502
2503 if (subtree.empty())
2504 {
2505 /* Entity manager is either still loading the configuration or
2506 * failed to load. In either way, return from here as the dbus
2507 * match will take care */
2508 std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
2509 return;
2510 }
2511
2512 /* There should be only one HSBP Configureation exposed */
2513 if (subtree.size() != 1)
2514 {
2515 std::cerr << __FUNCTION__
2516 << ": Multiple configurations "
2517 "detected !!\n";
2518 /* Critical Error. Stop Application */
2519 stopHsbpManager();
2520 return;
2521 }
2522
2523 auto& path = subtree.begin()->first;
2524 auto& objDict = subtree.begin()->second;
2525
2526 if (objDict.empty())
2527 {
2528 /* Critical Error. Stop Application */
2529 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2530 stopHsbpManager();
2531 return;
2532 }
2533
2534 const std::string& service = objDict.begin()->first;
2535
2536 conn->async_method_call(
2537 [](const boost::system::error_code er,
2538 const boost::container::flat_map<std::string,
2539 BasicVariantType>& resp) {
2540 if (er)
2541 {
2542 std::cerr << __FUNCTION__ << ": Error Getting Config "
2543 << er.message() << "\n";
2544 /* Critical Error. Stop Application */
2545 stopHsbpManager();
2546 return;
2547 }
2548
2549 std::optional<uint64_t> rootI2cBus;
2550 std::optional<std::vector<std::string>> supportedHsbps;
2551 std::optional<std::vector<std::string>> clockBufferTypes;
2552 std::optional<std::vector<std::string>> ioExpanderTypes;
2553
2554 /* Loop through to get root i2c bus and list of supported
2555 * HSBPs */
2556 for (const auto& [key, value] : resp)
2557 {
2558 if (key == "HsbpSupported")
2559 {
2560 supportedHsbps =
2561 std::get<std::vector<std::string>>(value);
2562 }
2563 else if (key == "RootI2cBus")
2564 {
2565 rootI2cBus = std::get<uint64_t>(value);
2566 }
2567 else if (key == "ClockBuffer")
2568 {
2569 clockBufferTypes =
2570 std::get<std::vector<std::string>>(value);
2571 }
2572 else if (key == "IoExpander")
2573 {
2574 ioExpanderTypes =
2575 std::get<std::vector<std::string>>(value);
2576 }
2577 }
2578
2579 /* Verify if i2c bus, supported HSBP's and clock buffers
2580 * were defined (IO Expanders are optional) */
2581 if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
2582 {
2583 std::cerr << __FUNCTION__
2584 << ": Incomplete HSBP "
2585 "configuration !! \n";
2586 /* Critical Error. Stop Application */
2587 stopHsbpManager();
2588 return;
2589 }
2590
2591 /* Clear and Load all details to global hsbp configuration
2592 * variable */
2593 hsbpConfig.clearConfig();
2594 hsbpConfig.rootBus = *rootI2cBus;
2595 hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
2596
2597 for (auto& clkBuffType : *clockBufferTypes)
2598 {
2599 hsbpConfig.clockBufferTypes.emplace_back(
2600 "xyz.openbmc_project.Configuration." + clkBuffType);
2601 }
2602
2603 if (ioExpanderTypes)
2604 {
2605 for (auto& ioCntrType : *ioExpanderTypes)
2606 {
2607 hsbpConfig.ioExpanderTypes.emplace_back(
2608 "xyz.openbmc_project.Configuration." +
2609 ioCntrType);
2610 }
2611 }
2612
2613 /* Loop through to get HSBP-NVME map and Components map
2614 * details */
2615 uint8_t hsbpMapCount = 0;
2616 for (const auto& [key, value] : resp)
2617 {
2618 if (std::find(hsbpConfig.supportedHsbps.begin(),
Jason M. Bills2b973a52025-07-31 11:06:04 -07002619 hsbpConfig.supportedHsbps.end(), key) !=
2620 hsbpConfig.supportedHsbps.end())
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002621 {
2622 std::optional<std::vector<std::string>> hsbpMap;
2623 hsbpMap = std::get<NvmeMapping>(value);
2624 if (!hsbpMap)
2625 {
2626 break;
2627 }
2628 hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
2629 hsbpMapCount++;
2630 }
2631 }
2632
2633 /* Check if we were able to get all the HSBP-NVMe maps */
2634 if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
2635 {
2636 std::cerr << __FUNCTION__
2637 << ": Incomplete HSBP Map "
2638 "details !! \n";
2639 /* Critical Error. Stop Application */
2640 stopHsbpManager();
2641 return;
2642 }
2643
2644 /* HSBP configuration is loaded */
2645 appState = AppState::hsbpConfigLoaded;
2646 std::cerr << "HSBP Config loaded !\n";
2647
2648 /* Get Clock buffers and IO expander details. Create shared
2649 * object of AsyncCallbackHandler with success and error
2650 * callback */
2651 auto componentsLoadedCallback = std::make_shared<
2652 AsyncCallbackHandler>(
2653 []() {
2654 /* Verify if all components were initialized without
2655 * errors */
2656 if (!verifyComponentsLoaded())
2657 {
2658 /* The application cannot proceed further as
2659 * components initialization failed. App needs
2660 * Restart */
2661 appState = AppState::idle;
2662 std::cerr
2663 << "One or more Componenets initialization "
2664 "failed !! Restart Required !\n";
2665 stopHsbpManager();
2666 }
2667
2668 appState = AppState::componentsLoaded;
2669 setUpBackplanesAndDrives();
2670 },
2671 []() {
2672 /* The application cannot proceed further as
2673 * components load failed. App needs Restart */
2674 appState = AppState::idle;
2675 std::cerr << "Loading Componenets failed !! "
2676 "Restart Required !\n";
2677 stopHsbpManager();
2678 });
2679
2680 loadClockBufferInfo(componentsLoadedCallback);
2681
2682 if (ioExpanderTypes)
2683 {
2684 loadIoExpanderInfo(componentsLoadedCallback);
2685 }
2686 },
2687 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2688 hsbpConfigIntf);
2689 },
2690 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2691 0, std::array<const char*, 1>{hsbpConfigIntf});
2692}
2693
2694void setupHsbpConfigMatch()
2695{
2696 static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
2697 *conn,
2698 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2699 "member='PropertiesChanged', "
2700 "interface='org.freedesktop.DBus.Properties', "
2701 "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2702 std::string(hsbpConfigIntf) + "'",
2703 [](sdbusplus::message_t& msg) {
2704 std::string intfName;
2705 boost::container::flat_map<std::string, BasicVariantType> values;
2706 msg.read(intfName, values);
2707
2708 /* This match will be triggered for each of the property being set
2709 * under the hsbpConfig interface. "HsbpSupported" is one of the
2710 * important property which will enable us to read other properties.
2711 * So, when the match event occurs for "HsbpSupported" property
2712 * being set, we will call "loadHsbpConfig()" If the control has
2713 * come here, its either the first initialization or entity-manager
2714 * reload. So, we will reset the state to uninitialized
2715 */
2716 for (const auto& [key, value] : values)
2717 {
2718 if (key == "HsbpSupported")
2719 {
2720 /* Configuration change detected, change the state to stop
2721 * other processing */
2722 appState = AppState::idle;
2723
2724 /* We will call the function after a small delay to let all
2725 * the properties to be intialized */
2726 auto loadTimer =
2727 std::make_shared<boost::asio::steady_timer>(io);
2728 loadTimer->expires_after(std::chrono::seconds(1));
2729 loadTimer->async_wait(
2730 [loadTimer](const boost::system::error_code ec) {
2731 if (ec == boost::asio::error::operation_aborted)
2732 {
2733 return;
2734 }
2735 else if (ec)
2736 {
2737 std::cerr << __FUNCTION__ << ": Timer error"
2738 << ec.message() << "\n";
2739 if (hsbpConfig.supportedHsbps.empty())
2740 {
2741 /* Critical Error as none of the
2742 * configuration was loaded and timer
2743 * failed. Stop the application */
2744 stopHsbpManager();
2745 }
2746 return;
2747 }
2748 loadHsbpConfig();
2749 });
2750 }
2751 }
2752 });
2753}
2754/***************************** End of Section *******************************/
2755
2756/****************************************************************************/
2757/***************** GPIO Events related Function Definitions *****************/
2758/****************************************************************************/
2759static void nvmeLvc3AlertHandler()
2760{
2761 /* If the state is not backplanesLoaded, we ignore the GPIO event as we
2762 * cannot communicate to the backplanes yet */
2763 if (appState < AppState::backplanesLoaded)
2764 {
2765 std::cerr << __FUNCTION__
2766 << ": HSBP not initialized ! Dropping the interrupt ! \n";
2767 return;
2768 }
2769
2770 /* This GPIO event only indicates the addition or removal of drive to either
2771 * of CPU. The backplanes detected need to be scanned and detect which drive
2772 * has been added/removed and enable/diable clock accordingly */
2773 gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
2774
2775 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
2776 {
2777 /* Check for HSBP Drives status to determine if any new drive has been
2778 * added/removed and update clocks accordingly */
2779 checkHsbpDrivesStatus();
2780 }
2781
2782 nvmeLvc3AlertEvent.async_wait(
2783 boost::asio::posix::stream_descriptor::wait_read,
2784 [](const boost::system::error_code ec) {
2785 if (ec)
2786 {
Jason M. Bills2b973a52025-07-31 11:06:04 -07002787 std::cerr << __FUNCTION__ << ": nvmealert event error: "
2788 << ec.message() << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002789 }
2790 nvmeLvc3AlertHandler();
2791 });
Feist, Jamesc95cf672019-08-29 16:10:35 -07002792}
2793
Rohit Chandel52639be2021-04-14 15:10:41 +05302794static bool hsbpRequestAlertGpioEvents(
2795 const std::string& name, const std::function<void()>& handler,
2796 gpiod::line& gpioLine,
2797 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
2798{
2799 // Find the GPIO line
2800 gpioLine = gpiod::find_line(name);
2801 if (!gpioLine)
2802 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002803 std::cerr << __FUNCTION__ << ": Failed to find the " << name
2804 << " line\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302805 return false;
2806 }
2807
2808 try
2809 {
2810 gpioLine.request(
2811 {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
2812 }
2813 catch (std::exception&)
2814 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002815 std::cerr << __FUNCTION__ << ": Failed to request events for " << name
2816 << "\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302817 return false;
2818 }
2819
2820 int gpioLineFd = gpioLine.event_get_fd();
2821 if (gpioLineFd < 0)
2822 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002823 std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302824 return false;
2825 }
2826
2827 gpioEventDescriptor.assign(gpioLineFd);
2828
2829 gpioEventDescriptor.async_wait(
2830 boost::asio::posix::stream_descriptor::wait_read,
2831 [&name, handler](const boost::system::error_code ec) {
2832 if (ec)
2833 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002834 std::cerr << __FUNCTION__ << ": " << name
2835 << " fd handler error: " << ec.message() << "\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302836 return;
2837 }
2838 handler();
2839 });
2840 return true;
2841}
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002842/***************************** End of Section *******************************/
Rohit Chandel52639be2021-04-14 15:10:41 +05302843
Feist, Jamesc95cf672019-08-29 16:10:35 -07002844int main()
2845{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002846 std::cerr << "******* Starting hsbp-manager *******\n";
Feist, Jamesc95cf672019-08-29 16:10:35 -07002847
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002848 /* Set the Dbus name */
James Feistdb2e0e72019-10-07 16:34:06 -07002849 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07002850
Dileep Kumar Choppa373cf3b2023-08-18 13:05:36 +05302851 std::shared_ptr<sdbusplus::asio::dbus_interface> storageIface;
2852
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002853 /* Add interface for storage inventory */
Jayaprakash Mutyala9cefb822025-05-09 17:51:18 +00002854 storageIface = objServer.add_interface(
2855 "/xyz/openbmc_project/inventory/item/storage/hsbp/1",
2856 "xyz.openbmc_project.Inventory.Item.Storage");
Feist, Jamesc95cf672019-08-29 16:10:35 -07002857
Dileep Kumar Choppa373cf3b2023-08-18 13:05:36 +05302858 storageIface->initialize();
2859
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002860 /* HSBP initializtion flow:
2861 * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
2862 * 2. Set up Dbus match for power - determine if host is up and running
2863 * or powered off
2864 * 3. Set up Dbus match for HSBP backplanes and Drives
2865 * 4. Load HSBP config exposed by entity manager
2866 * - Also setup a match to capture HSBP configuation in case
2867 * entity-manager restarts
2868 * 5. Load Clock buffer and IO expander (and other peripherals if any
2869 * related to HSBP functionality)
2870 * - Reload the info each time HSBP configuration is changed
2871 * 6. Populate all Backpanes (HSBP's)
2872 * 7. Load all NVMe drive's and associate with HSBP Backpane
2873 */
James Feist0b236ab2019-10-02 09:09:16 -07002874
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002875 /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
2876 if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
2877 nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
2878 nvmeLvc3AlertEvent))
Rohit Chandel52639be2021-04-14 15:10:41 +05302879 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002880 std::cerr << __FUNCTION__
2881 << ": error: Unable to monitor events on HSBP "
2882 "Alert line\n";
2883 return -1;
Rohit Chandel52639be2021-04-14 15:10:41 +05302884 }
2885
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002886 /* Setup Dbus-match for power */
James Feist9f6565d2019-10-09 13:15:13 -07002887 setupPowerMatch(conn);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002888
2889 /* Setup Dbus-match for HSBP backplanes and Drives */
2890 setupBackplanesAndDrivesMatch();
2891
2892 /* Setup HSBP Config match and load config
2893 * In the event of entity-manager reboot, the match will help catch new
2894 * configuration.
2895 * In the event of hsbp-manager reboot, loadHsbpConfig will get all
2896 * config details and will take care of remaining config's to be
2897 * loaded
2898 */
2899 setupHsbpConfigMatch();
2900 loadHsbpConfig();
2901
Feist, Jamesc95cf672019-08-29 16:10:35 -07002902 io.run();
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002903 std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
2904 return -1;
Feist, Jamesc95cf672019-08-29 16:10:35 -07002905}