blob: b3b1ff51db8973478c5816f9f0b825e49cc4f6bd [file] [log] [blame]
Feist, Jamesc95cf672019-08-29 16:10:35 -07001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "utils.hpp"
18
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080019#include <algorithm>
Rohit Chandel52639be2021-04-14 15:10:41 +053020#include <bitset>
Feist, Jamesc95cf672019-08-29 16:10:35 -070021#include <boost/algorithm/string/replace.hpp>
Rohit Chandel52639be2021-04-14 15:10:41 +053022#include <boost/asio/posix/stream_descriptor.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070023#include <boost/asio/steady_timer.hpp>
James Feist8675a912019-10-16 14:36:58 -070024#include <boost/container/flat_set.hpp>
James Feist0b236ab2019-10-02 09:09:16 -070025#include <filesystem>
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080026#include <forward_list>
James Feist8675a912019-10-16 14:36:58 -070027#include <fstream>
Rohit Chandel52639be2021-04-14 15:10:41 +053028#include <gpiod.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070029#include <iostream>
Jason M. Billsfe601722023-04-04 09:48:34 -070030#include <list>
Feist, Jamesc95cf672019-08-29 16:10:35 -070031#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
33#include <sdbusplus/bus/match.hpp>
34#include <string>
James Feist45772222019-09-27 10:38:08 -070035#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070036
37extern "C" {
38#include <i2c/smbus.h>
39#include <linux/i2c-dev.h>
40}
41
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080042/****************************************************************************/
43/******************** Global Constants/Type Declarations ********************/
44/****************************************************************************/
45constexpr const char* hsbpCpldInft =
Feist, Jamesc95cf672019-08-29 16:10:35 -070046 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080047constexpr const char* hsbpConfigIntf =
48 "xyz.openbmc_project.Configuration.HSBPConfiguration";
49constexpr const char* nvmeIntf = "xyz.openbmc_project.Inventory.Item.NVMe";
James Feistdb2e0e72019-10-07 16:34:06 -070050constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
Feist, Jamesc95cf672019-08-29 16:10:35 -070051
James Feist45772222019-09-27 10:38:08 -070052constexpr size_t scanRateSeconds = 5;
53constexpr size_t maxDrives = 8; // only 1 byte alloted
54
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -080055using NvmeMapping = std::vector<std::string>;
56/***************************** End of Section *******************************/
57
58/****************************************************************************/
59/**************************** Enums Definitions *****************************/
60/****************************************************************************/
61enum class AppState : uint8_t
62{
63 idle,
64 loadingHsbpConfig,
65 hsbpConfigLoaded,
66 loadingComponents,
67 componentsLoaded,
68 loadingBackplanes,
69 backplanesLoaded,
70 loadingDrives,
71 drivesLoaded
72};
73
74enum class BlinkPattern : uint8_t
75{
76 off = 0x0,
77 error = 0x2,
78 terminate = 0x3
79};
80/***************************** End of Section *******************************/
81
82/****************************************************************************/
83/************ HSBP Configuration related struct/class Definitions ***********/
84/****************************************************************************/
85struct HsbpConfig
86{
87 size_t rootBus;
88 std::vector<std::string> supportedHsbps;
89 std::unordered_map<std::string, NvmeMapping> hsbpNvmeMap;
90 std::vector<std::string> clockBufferTypes;
91 std::vector<std::string> ioExpanderTypes;
92
93 void clearConfig()
94 {
95 rootBus = -1;
96 supportedHsbps.clear();
97 hsbpNvmeMap.clear();
98 clockBufferTypes.clear();
99 ioExpanderTypes.clear();
100 }
101};
102
103class ClockBuffer
104{
105 size_t bus;
106 size_t address;
107 std::string modeOfOperation;
108 size_t outCtrlBaseAddr;
109 size_t outCtrlByteCount;
110 std::unordered_map<std::string, std::vector<std::string>> byteMap;
111 std::string name;
112 std::string type;
113
114 int file = -1;
115 bool initialized = false;
116
117 void initialize()
118 {
119 /* Execute below operation only when mode of operation is SMBus. By
120 * default the clock buffer is configured to follow OE pin output, so we
121 * need to set the output value to 0 to disable the clock outputs. If
122 * mode of operation is IO, then the IO value will determine the
123 * disable/enable of clock output */
124 if (modeOfOperation == "SMBus")
125 {
126 if (file < 0)
127 {
128 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
129 O_RDWR | O_CLOEXEC);
130 if (file < 0)
131 {
132 std::cerr << "ClockBuffer : \"" << name
133 << "\" - Unable to open bus : " << bus << "\n";
134 return;
135 }
136 }
137
138 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
139 {
140 std::cerr << "ClockBuffer : \"" << name
141 << "\" - Unable to set address to " << address
142 << "\n";
143 return;
144 }
145
146 for (uint8_t i = 0; i < outCtrlByteCount; i++)
147 {
148 std::string byteName = "Byte" + std::to_string(i);
149
150 auto byte = byteMap.find(byteName);
151 if (byte == byteMap.end())
152 {
153 std::cerr << "ClockBuffer : \"" << name
154 << "\" - Byte map error ! Unable to find "
155 << byteName << "\n";
156 return;
157 }
158
159 /* Get current value of output control register */
160 int read = i2c_smbus_read_byte_data(
161 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
162 if (read < 0)
163 {
164 std::cerr << "ClockBuffer : \"" << name
165 << "\" - Error: Unable to read data from clock "
166 "buffer register\n";
167 return;
168 }
169
170 std::bitset<8> currByte(read);
171
172 /* Set zero only at bit position that we have a NVMe drive (i.e.
173 * ignore where byteMap is "-"). We do not want to touch other
174 * bits */
175 for (uint8_t bit = 0; bit < 8; bit++)
176 {
177 if (byte->second.at(bit) != "-")
178 {
179 currByte.reset(bit);
180 }
181 }
182
183 int ret = i2c_smbus_write_byte_data(
184 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
185 static_cast<uint8_t>(currByte.to_ulong()));
186
187 if (ret < 0)
188 {
189 std::cerr << "ClockBuffer : \"" << name
190 << "\" - Error: Unable to write data to clock "
191 "buffer register\n";
192 return;
193 }
194 }
195 }
196 initialized = true;
197 std::cerr << "ClockBuffer : \"" << name << "\" initialized\n";
198 }
199
200 public:
201 ClockBuffer(
202 size_t busIn, size_t addressIn, std::string& modeOfOperationIn,
203 size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
204 std::unordered_map<std::string, std::vector<std::string>>& byteMapIn,
205 std::string& nameIn, std::string& typeIn) :
206 bus(busIn),
207 address(addressIn), modeOfOperation(std::move(modeOfOperationIn)),
208 outCtrlBaseAddr(outCtrlBaseAddrIn),
209 outCtrlByteCount(outCtrlByteCountIn), byteMap(std::move(byteMapIn)),
210 name(std::move(nameIn)), type(std::move(typeIn))
211 {
212 initialize();
213 }
214
215 bool isInitialized()
216 {
217 if (!initialized)
218 {
219 /* There was an issue with the initialization of this component. Try
220 * to invoke initialization again */
221 initialize();
222 }
223 return initialized;
224 }
225
226 std::string getName()
227 {
228 return name;
229 }
230
231 bool enableDisableClock(std::forward_list<std::string>& nvmeDrivesInserted,
232 std::forward_list<std::string>& nvmeDrivesRemoved)
233 {
234 if (modeOfOperation != "SMBus")
235 {
236 /* The clock is enabled using IO expander. No action needed from
237 * here */
238 return true;
239 }
240
241 if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
242 {
243 /* There are no drives to update */
244 return true;
245 }
246
247 for (uint8_t i = 0; i < outCtrlByteCount; i++)
248 {
249 std::string byteName = "Byte" + std::to_string(i);
250
251 auto byte = byteMap.find(byteName);
252 if (byte == byteMap.end())
253 {
254 std::cerr << "ClockBuffer : \"" << name
255 << "\" - Byte map error ! Unable to find " << byteName
256 << "\n";
257 return false;
258 }
259
260 /* Get current value of output control register */
261 int read = i2c_smbus_read_byte_data(
262 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
263 if (read < 0)
264 {
265 std::cerr << "ClockBuffer : \"" << name
266 << "\" - Error: Unable to read data from clock "
267 "buffer register\n";
268 return false;
269 }
270
271 std::bitset<8> currByte(read);
272 bool writeRequired = false;
273
274 /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
275 * reset the bit if found in nvmeDrivesRemoved */
276 for (uint8_t bit = 0; bit < 8; bit++)
277 {
278 /* The remove function returns number of elements removed from
279 * list indicating the presence of the drive and also removing
280 * it form the list */
281 if (nvmeDrivesInserted.remove(byte->second.at(bit)))
282 {
283 writeRequired = true;
284 currByte.set(bit);
285 continue;
286 }
287
288 if (nvmeDrivesRemoved.remove(byte->second.at(bit)))
289 {
290 writeRequired = true;
291 currByte.reset(bit);
292 }
293 }
294
295 if (!writeRequired)
296 {
297 /* No Write is required as there are no changes */
298 continue;
299 }
300
301 int ret = i2c_smbus_write_byte_data(
302 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
303 static_cast<uint8_t>(currByte.to_ulong()));
304 if (ret < 0)
305 {
306 std::cerr << "ClockBuffer : \"" << name
307 << "\" - Error: Unable to write data to clock "
308 "buffer register\n";
309 return false;
310 }
311 }
312
313 return true;
314 }
315
316 ~ClockBuffer()
317 {
318 if (file >= 0)
319 {
320 close(file);
321 }
322 }
323};
324
325class IoExpander
326{
327 size_t bus;
328 size_t address;
329 size_t confIORegAddr;
330 size_t outCtrlBaseAddr;
331 size_t outCtrlByteCount;
332 std::unordered_map<std::string, std::vector<std::string>> ioMap;
333 std::string name;
334 std::string type;
335
336 int file = -1;
337 bool initialized = false;
338
339 void initialize()
340 {
341 /* Initialize the IO expander Control register to configure the IO ports
342 * as outputs and set the output to low by default */
343 if (file < 0)
344 {
345 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
346 O_RDWR | O_CLOEXEC);
347 if (file < 0)
348 {
349 std::cerr << "IoExpander : " << name
350 << " - Unable to open bus : " << bus << "\n";
351 return;
352 }
353 }
354
355 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
356 {
357 std::cerr << "IoExpander : \"" << name
358 << "\" - Unable to set address to " << address << "\n";
359 return;
360 }
361
362 for (uint8_t i = 0; i < outCtrlByteCount; i++)
363 {
364 std::string ioName = "IO" + std::to_string(i);
365
366 auto io = ioMap.find(ioName);
367 if (io == ioMap.end())
368 {
369 std::cerr << "IoExpander : \"" << name
370 << "\" - IO map error ! Unable to find " << ioName
371 << "\n";
372 return;
373 }
374
375 /* Get current value of IO configuration register */
376 int read1 = i2c_smbus_read_byte_data(
377 file, static_cast<uint8_t>(confIORegAddr + i));
378 if (read1 < 0)
379 {
380 std::cerr << "IoExpander : \"" << name
381 << "\" - Error: Unable to read data from io expander "
382 "IO control register\n";
383 return;
384 }
385
386 /* Get current value of IO Ouput register */
387 int read2 = i2c_smbus_read_byte_data(
388 file, static_cast<uint8_t>(confIORegAddr + i));
389 if (read2 < 0)
390 {
391 std::cerr << "IoExpander : \"" << name
392 << "\" - Error: Unable to read data from io expander "
393 "IO output register\n";
394 return;
395 }
396
397 std::bitset<8> currCtrlVal(read1);
398 std::bitset<8> currOutVal(read2);
399
400 /* Set zero only at bit position that we have a NVMe drive (i.e.
401 * ignore where ioMap is "-"). We do not want to touch other
402 * bits */
403 for (uint8_t bit = 0; bit < 8; bit++)
404 {
405 if (io->second.at(bit) != "-")
406 {
407 currCtrlVal.reset(bit);
408 currOutVal.reset(bit);
409 }
410 }
411
412 int ret1 = i2c_smbus_write_byte_data(
413 file, static_cast<uint8_t>(confIORegAddr + i),
414 static_cast<uint8_t>(currCtrlVal.to_ulong()));
415 if (ret1 < 0)
416 {
417 std::cerr << "IoExpander : \"" << name
418 << "\" - Error: Unable to write data to IO expander "
419 "IO control register\n";
420 return;
421 }
422
423 int ret2 = i2c_smbus_write_byte_data(
424 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
425 static_cast<uint8_t>(currOutVal.to_ulong()));
426 if (ret2 < 0)
427 {
428 std::cerr << "IoExpander : \"" << name
429 << "\" - Error: Unable to write data to IO expander "
430 "IO output register\n";
431 return;
432 }
433 }
434 initialized = true;
435 std::cerr << "IoExpander : \"" << name << "\" initialized\n";
436 }
437
438 public:
439 IoExpander(
440 size_t busIn, size_t addressIn, size_t confIORegAddrIn,
441 size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
442 std::unordered_map<std::string, std::vector<std::string>>& ioMapIn,
443 std::string& nameIn, std::string& typeIn) :
444 bus(busIn),
445 address(addressIn), confIORegAddr(confIORegAddrIn),
446 outCtrlBaseAddr(outCtrlBaseAddrIn),
447 outCtrlByteCount(outCtrlByteCountIn), ioMap(std::move(ioMapIn)),
448 name(std::move(nameIn)), type(std::move(typeIn))
449 {
450 initialize();
451 }
452
453 bool isInitialized()
454 {
455 if (!initialized)
456 {
457 /* There was an issue with the initialization of this component. Try
458 * to invoke initialization again */
459 initialize();
460 }
461 return initialized;
462 }
463
464 std::string getName()
465 {
466 return name;
467 }
468
469 bool enableDisableOuput(std::forward_list<std::string>& nvmeDrivesInserted,
470 std::forward_list<std::string>& nvmeDrivesRemoved)
471 {
472 if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
473 {
474 /* There are no drives to update */
475 return true;
476 }
477
478 for (uint8_t i = 0; i < outCtrlByteCount; i++)
479 {
480 std::string ioName = "IO" + std::to_string(i);
481
482 auto io = ioMap.find(ioName);
483 if (io == ioMap.end())
484 {
485 std::cerr << "IoExpander : \"" << name
486 << "\" - IO map error ! Unable to find " << ioName
487 << "\n";
488 return false;
489 }
490
491 /* Get current value of IO output register */
492 int read = i2c_smbus_read_byte_data(
493 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
494 if (read < 0)
495 {
496 std::cerr << "IoExpander : \"" << name
497 << "\" - Error: Unable to read data from io expander "
498 "register\n";
499 return false;
500 }
501
502 std::bitset<8> currVal(read);
503 bool writeRequired = false;
504
505 /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
506 * reset the bit if found in nvmeDrivesRemoved */
507 for (uint8_t bit = 0; bit < 8; bit++)
508 {
509 /* The remove function returns number of elements removed from
510 * list indicating the presence of the drive and also removing
511 * it form the list */
512 if (nvmeDrivesInserted.remove(io->second.at(bit)))
513 {
514 writeRequired = true;
515 currVal.set(bit);
516 continue;
517 }
518
519 if (nvmeDrivesRemoved.remove(io->second.at(bit)))
520 {
521 writeRequired = true;
522 currVal.reset(bit);
523 }
524 }
525
526 if (!writeRequired)
527 {
528 /* No Write is required as there are no changes */
529 continue;
530 }
531
532 int ret = i2c_smbus_write_byte_data(
533 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
534 static_cast<uint8_t>(currVal.to_ulong()));
535 if (ret < 0)
536 {
537 std::cerr << "IoExpander : \"" << name
538 << "\" - Error: Unable to write data to IO expander "
539 "register\n";
540 return false;
541 }
542 }
543
544 return true;
545 }
546
547 ~IoExpander()
548 {
549 if (file >= 0)
550 {
551 close(file);
552 }
553 }
554};
555/***************************** End of Section *******************************/
556
557/****************************************************************************/
558/*********************** Global Variables Declarations **********************/
559/****************************************************************************/
560/* State os Application */
561static AppState appState = AppState::idle;
562
563/* Configuration and Components */
564static HsbpConfig hsbpConfig;
565std::forward_list<ClockBuffer> clockBuffers;
566std::forward_list<IoExpander> ioExpanders;
567
568/* Boost IO context and Dbus variables */
Feist, Jamesc95cf672019-08-29 16:10:35 -0700569boost::asio::io_context io;
570auto conn = std::make_shared<sdbusplus::asio::connection>(io);
571sdbusplus::asio::object_server objServer(conn);
572
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800573/* GPIO Lines and GPIO Event Descriptors */
Rohit Chandel52639be2021-04-14 15:10:41 +0530574static gpiod::line nvmeLvc3AlertLine;
575static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800576/***************************** End of Section *******************************/
Rohit Chandel52639be2021-04-14 15:10:41 +0530577
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800578/****************************************************************************/
579/********** HSBP Backplane related struct and Global definitions ************/
580/****************************************************************************/
James Feist0b236ab2019-10-02 09:09:16 -0700581struct Mux
582{
James Feist8675a912019-10-16 14:36:58 -0700583 Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
584 bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
James Feist0b236ab2019-10-02 09:09:16 -0700585 {
586 }
587 size_t bus;
588 size_t address;
James Feist8675a912019-10-16 14:36:58 -0700589 size_t channels;
590 size_t index;
591
592 // to sort in the flat set
593 bool operator<(const Mux& rhs) const
594 {
595 return index < rhs.index;
596 }
James Feist0b236ab2019-10-02 09:09:16 -0700597};
James Feist09dd2312019-10-09 09:29:03 -0700598
James Feist09dd2312019-10-09 09:29:03 -0700599struct Led : std::enable_shared_from_this<Led>
600{
601 // led pattern addresses start at 0x10
602 Led(const std::string& path, size_t index, int fd) :
603 address(static_cast<uint8_t>(index + 0x10)), file(fd),
604 ledInterface(objServer.add_interface(path, ledGroup::interface))
605 {
606 if (index >= maxDrives)
607 {
608 throw std::runtime_error("Invalid drive index");
609 }
610
611 if (!set(BlinkPattern::off))
612 {
613 std::cerr << "Cannot initialize LED " << path << "\n";
614 }
615 }
616
617 // this has to be called outside the constructor for shared_from_this to
618 // work
619 void createInterface(void)
620 {
James Feist09dd2312019-10-09 09:29:03 -0700621 ledInterface->register_property(
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530622 ledGroup::asserted, false,
623 [weakRef{weak_from_this()}](const bool req, bool& val) {
624 auto self = weakRef.lock();
625 if (!self)
626 {
627 return 0;
628 }
James Feist09dd2312019-10-09 09:29:03 -0700629 if (req == val)
630 {
631 return 1;
632 }
James Feist9f6565d2019-10-09 13:15:13 -0700633
634 if (!isPowerOn())
635 {
636 std::cerr << "Can't change blink state when power is off\n";
637 throw std::runtime_error(
638 "Can't change blink state when power is off");
639 }
James Feist09dd2312019-10-09 09:29:03 -0700640 BlinkPattern pattern =
641 req ? BlinkPattern::error : BlinkPattern::terminate;
642 if (!self->set(pattern))
643 {
James Feist9f6565d2019-10-09 13:15:13 -0700644 std::cerr << "Can't change blink pattern\n";
James Feist09dd2312019-10-09 09:29:03 -0700645 throw std::runtime_error("Cannot set blink pattern");
646 }
647 val = req;
648 return 1;
649 });
650 ledInterface->initialize();
651 }
652
653 virtual ~Led()
654 {
655 objServer.remove_interface(ledInterface);
656 }
657
658 bool set(BlinkPattern pattern)
659 {
660 int ret = i2c_smbus_write_byte_data(file, address,
661 static_cast<uint8_t>(pattern));
662 return ret >= 0;
663 }
664
665 uint8_t address;
666 int file;
667 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
668};
669
James Feist45772222019-09-27 10:38:08 -0700670struct Drive
671{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800672 Drive(std::string driveName, bool present, bool isOperational, bool nvme,
James Feist244f3232019-09-27 15:15:14 -0700673 bool rebuilding) :
James Feist42b49c12019-10-29 15:18:43 -0700674 isNvme(nvme),
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800675 isPresent(present), name(driveName)
James Feist45772222019-09-27 10:38:08 -0700676 {
677 constexpr const char* basePath =
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800678 "/xyz/openbmc_project/inventory/item/drive/";
679 itemIface =
680 objServer.add_interface(basePath + driveName, inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700681 itemIface->register_property("Present", isPresent);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800682 itemIface->register_property("PrettyName", driveName);
James Feist45772222019-09-27 10:38:08 -0700683 itemIface->initialize();
684 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700685 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700686 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feist42b49c12019-10-29 15:18:43 -0700687
688 operationalIface->register_property(
689 "Functional", isOperational,
690 [this](const bool req, bool& property) {
691 if (!isPresent)
692 {
693 return 0;
694 }
695 if (property == req)
696 {
697 return 1;
698 }
699 property = req;
700 if (req)
701 {
702 clearFailed();
703 return 1;
704 }
705 markFailed();
706 return 1;
707 });
708
James Feist45772222019-09-27 10:38:08 -0700709 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700710 rebuildingIface = objServer.add_interface(
711 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
712 rebuildingIface->register_property("Rebuilding", rebuilding);
713 rebuildingIface->initialize();
James Feist8675a912019-10-16 14:36:58 -0700714 driveIface =
715 objServer.add_interface(itemIface->get_object_path(),
716 "xyz.openbmc_project.Inventory.Item.Drive");
717 driveIface->initialize();
James Feist42b49c12019-10-29 15:18:43 -0700718 associations = objServer.add_interface(itemIface->get_object_path(),
719 association::interface);
720 associations->register_property("Associations",
721 std::vector<Association>{});
722 associations->initialize();
723
724 if (isPresent && (!isOperational || rebuilding))
725 {
726 markFailed();
727 }
James Feist45772222019-09-27 10:38:08 -0700728 }
James Feist09dd2312019-10-09 09:29:03 -0700729 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700730 {
731 objServer.remove_interface(itemIface);
732 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700733 objServer.remove_interface(rebuildingIface);
James Feist8675a912019-10-16 14:36:58 -0700734 objServer.remove_interface(assetIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700735 objServer.remove_interface(driveIface);
James Feist42b49c12019-10-29 15:18:43 -0700736 objServer.remove_interface(associations);
James Feist0b236ab2019-10-02 09:09:16 -0700737 }
738
James Feistc66735b2020-07-17 13:51:21 -0700739 void removeAsset()
740 {
741 objServer.remove_interface(assetIface);
742 assetIface = nullptr;
743 }
744
James Feist8675a912019-10-16 14:36:58 -0700745 void createAsset(
746 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700747 {
James Feist8675a912019-10-16 14:36:58 -0700748 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700749 {
750 return;
751 }
James Feist8675a912019-10-16 14:36:58 -0700752 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700753 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700754 "xyz.openbmc_project.Inventory.Decorator.Asset");
755 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700756 {
James Feist8675a912019-10-16 14:36:58 -0700757 assetIface->register_property(key, value);
James Feistd86629c2020-04-23 10:07:25 -0700758 if (key == "SerialNumber")
759 {
760 serialNumber = value;
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700761 serialNumberInitialized = true;
James Feistd86629c2020-04-23 10:07:25 -0700762 }
James Feistdb2e0e72019-10-07 16:34:06 -0700763 }
James Feist8675a912019-10-16 14:36:58 -0700764 assetIface->initialize();
James Feistdb2e0e72019-10-07 16:34:06 -0700765 }
766
James Feist42b49c12019-10-29 15:18:43 -0700767 void markFailed(void)
768 {
769 // todo: maybe look this up via mapper
770 constexpr const char* globalInventoryPath =
771 "/xyz/openbmc_project/CallbackManager";
772
773 if (!isPresent)
774 {
775 return;
776 }
777
778 operationalIface->set_property("Functional", false);
779 std::vector<Association> warning = {
780 {"", "warning", globalInventoryPath}};
781 associations->set_property("Associations", warning);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800782 logDriveError("Drive " + name);
James Feist42b49c12019-10-29 15:18:43 -0700783 }
784
785 void clearFailed(void)
786 {
787 operationalIface->set_property("Functional", true);
788 associations->set_property("Associations", std::vector<Association>{});
789 }
790
James Feistd86629c2020-04-23 10:07:25 -0700791 void setPresent(bool set)
James Feiste8818522019-11-04 13:36:10 -0800792 {
793 // nvme drives get detected by their fru
James Feistd86629c2020-04-23 10:07:25 -0700794 if (set == isPresent)
James Feiste8818522019-11-04 13:36:10 -0800795 {
796 return;
797 }
798 itemIface->set_property("Present", set);
799 isPresent = set;
James Feistd86629c2020-04-23 10:07:25 -0700800 }
801
802 void logPresent()
803 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700804 if (isNvme && !serialNumberInitialized)
James Feiste8818522019-11-04 13:36:10 -0800805 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700806 // wait until NVMe asset is updated to include the serial number
807 // from the NVMe drive
James Feistd86629c2020-04-23 10:07:25 -0700808 return;
James Feiste8818522019-11-04 13:36:10 -0800809 }
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700810
811 if (!isPresent && loggedPresent)
812 {
813 loggedPresent = false;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800814 logDeviceRemoved("Drive", name, serialNumber);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700815 serialNumber = "N/A";
816 serialNumberInitialized = false;
James Feistc66735b2020-07-17 13:51:21 -0700817 removeAsset();
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700818 }
819 else if (isPresent && !loggedPresent)
820 {
821 loggedPresent = true;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800822 logDeviceAdded("Drive", name, serialNumber);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700823 }
James Feiste8818522019-11-04 13:36:10 -0800824 }
825
James Feist45772222019-09-27 10:38:08 -0700826 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
827 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700828 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700829 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700830 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700831 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700832
James Feist45772222019-09-27 10:38:08 -0700833 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700834 bool isPresent;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800835 std::string name;
James Feistd86629c2020-04-23 10:07:25 -0700836 std::string serialNumber = "N/A";
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700837 bool serialNumberInitialized = false;
James Feistd86629c2020-04-23 10:07:25 -0700838 bool loggedPresent = false;
James Feist45772222019-09-27 10:38:08 -0700839};
840
James Feistd86629c2020-04-23 10:07:25 -0700841struct Backplane : std::enable_shared_from_this<Backplane>
Feist, Jamesc95cf672019-08-29 16:10:35 -0700842{
843
James Feist45772222019-09-27 10:38:08 -0700844 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
845 const std::string& nameIn) :
846 bus(busIn),
847 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feistd86629c2020-04-23 10:07:25 -0700848 timer(boost::asio::steady_timer(io)),
James Feist8675a912019-10-16 14:36:58 -0700849 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700850 {
851 }
James Feistd0d36f12019-11-21 10:19:44 -0800852 void populateAsset(const std::string& rootPath, const std::string& busname)
853 {
854 conn->async_method_call(
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530855 [assetIface{assetInterface}](
James Feistd0d36f12019-11-21 10:19:44 -0800856 const boost::system::error_code ec,
857 const boost::container::flat_map<
858 std::string, std::variant<std::string>>& values) mutable {
859 if (ec)
860 {
861 std::cerr
862 << "Error getting asset tag from HSBP configuration\n";
863
864 return;
865 }
James Feistd0d36f12019-11-21 10:19:44 -0800866 for (const auto& [key, value] : values)
867 {
868 const std::string* ptr = std::get_if<std::string>(&value);
869 if (ptr == nullptr)
870 {
871 std::cerr << key << " Invalid type!\n";
872 continue;
873 }
874 assetIface->register_property(key, *ptr);
875 }
876 assetIface->initialize();
877 },
878 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
879 assetTag);
880 }
881
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -0800882 static std::string zeroPad(const uint8_t val)
883 {
884 std::ostringstream version;
885 version << std::setw(2) << std::setfill('0')
886 << static_cast<size_t>(val);
887 return version.str();
888 }
889
James Feistd0d36f12019-11-21 10:19:44 -0800890 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700891 {
James Feist09dd2312019-10-09 09:29:03 -0700892 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
893 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700894 if (file < 0)
895 {
896 std::cerr << "unable to open bus " << bus << "\n";
897 return;
898 }
899
900 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
901 {
902 std::cerr << "unable to set address to " << address << "\n";
903 return;
904 }
905
James Feist45772222019-09-27 10:38:08 -0700906 if (!getPresent())
907 {
908 std::cerr << "Cannot detect CPLD\n";
909 return;
910 }
911
912 getBootVer(bootVer);
913 getFPGAVer(fpgaVer);
914 getSecurityRev(securityRev);
915 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700916 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700917 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700918 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700919 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700920 hsbpItemIface->register_property("PrettyName", name);
921 hsbpItemIface->initialize();
922
James Feistd0d36f12019-11-21 10:19:44 -0800923 storageInterface = objServer.add_interface(
924 hsbpItemIface->get_object_path(),
925 "xyz.openbmc_project.Inventory.Item.StorageController");
926 storageInterface->initialize();
927
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530928 assetInterface =
929 objServer.add_interface(hsbpItemIface->get_object_path(), assetTag);
930
James Feist45772222019-09-27 10:38:08 -0700931 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800932 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700933 "xyz.openbmc_project.Software.Version");
934 versionIface->register_property("Version", zeroPad(bootVer) + "." +
935 zeroPad(fpgaVer) + "." +
936 zeroPad(securityRev));
937 versionIface->register_property(
938 "Purpose",
939 std::string(
940 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
941 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000942
Vikash Chandolaad11f7d2023-04-28 17:23:52 +0530943 activationIface =
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000944 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
945 "xyz.openbmc_project.Software.Activation");
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000946 activationIface->register_property(
947 "Activation",
948 std::string(
949 "xyz.openbmc_project.Software.Activation.Activations.Active"));
950 activationIface->register_property(
951 "RequestedActivation",
952 std::string("xyz.openbmc_project.Software.Activation."
953 "RequestedActivations.None"));
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000954 activationIface->initialize();
955
James Feist45772222019-09-27 10:38:08 -0700956 getPresence(presence);
957 getIFDET(ifdet);
958
James Feistd0d36f12019-11-21 10:19:44 -0800959 populateAsset(rootPath, busname);
960
James Feist45772222019-09-27 10:38:08 -0700961 createDrives();
962
963 runTimer();
964 }
965
966 void runTimer()
967 {
James Feistd86629c2020-04-23 10:07:25 -0700968 timer.expires_after(std::chrono::seconds(scanRateSeconds));
969 timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
970 boost::system::error_code ec) {
971 auto self = weak.lock();
972 if (!self)
973 {
974 return;
975 }
James Feist45772222019-09-27 10:38:08 -0700976 if (ec == boost::asio::error::operation_aborted)
977 {
978 // we're being destroyed
979 return;
980 }
981 else if (ec)
982 {
983 std::cerr << "timer error " << ec.message() << "\n";
984 return;
985 }
James Feist9f6565d2019-10-09 13:15:13 -0700986
987 if (!isPowerOn())
988 {
989 // can't access hsbp when power is off
James Feistd86629c2020-04-23 10:07:25 -0700990 self->runTimer();
James Feist9f6565d2019-10-09 13:15:13 -0700991 return;
992 }
James Feist45772222019-09-27 10:38:08 -0700993
James Feistd86629c2020-04-23 10:07:25 -0700994 self->getPresence(self->presence);
995 self->getIFDET(self->ifdet);
996 self->getFailed(self->failed);
997 self->getRebuild(self->rebuilding);
James Feist45772222019-09-27 10:38:08 -0700998
James Feistd86629c2020-04-23 10:07:25 -0700999 self->updateDrives();
1000 self->runTimer();
James Feist45772222019-09-27 10:38:08 -07001001 });
1002 }
1003
1004 void createDrives()
1005 {
James Feist45772222019-09-27 10:38:08 -07001006 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001007 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001008 uint8_t driveSlot = (1 << ii);
1009 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1010 bool isPresent = isNvme || (presence & driveSlot);
1011 bool isFailed = !isPresent || failed & driveSlot;
1012 bool isRebuilding = !isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -07001013
1014 // +1 to convert from 0 based to 1 based
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001015 std::string driveName = boost::replace_all_copy(name, " ", "_") +
1016 "_Drive_" + std::to_string(ii + 1);
1017 Drive& drive = drives.emplace_back(driveName, isPresent, !isFailed,
James Feist09dd2312019-10-09 09:29:03 -07001018 isNvme, isRebuilding);
1019 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
1020 drive.itemIface->get_object_path(), ii, file));
1021 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001022 }
1023 }
1024
James Feist45772222019-09-27 10:38:08 -07001025 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -07001026 {
James Feist42b49c12019-10-29 15:18:43 -07001027 size_t ii = 0;
1028
1029 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001030 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001031 uint8_t driveSlot = (1 << ii);
1032 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1033 bool isPresent = isNvme || (presence & driveSlot);
1034 bool isFailed = !isPresent || (failed & driveSlot);
1035 bool isRebuilding = isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -07001036
James Feist42b49c12019-10-29 15:18:43 -07001037 it->isNvme = isNvme;
James Feistd86629c2020-04-23 10:07:25 -07001038 it->setPresent(isPresent);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -07001039 it->logPresent();
James Feistda0c35f2020-02-03 10:16:13 -08001040
James Feist42b49c12019-10-29 15:18:43 -07001041 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
1042 if (isFailed || isRebuilding)
1043 {
1044 it->markFailed();
1045 }
1046 else
1047 {
1048 it->clearFailed();
1049 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001050 }
James Feist45772222019-09-27 10:38:08 -07001051 }
1052
1053 bool getPresent()
1054 {
1055 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001056 return present;
1057 }
James Feist45772222019-09-27 10:38:08 -07001058
1059 bool getTypeID(uint8_t& val)
1060 {
1061 constexpr uint8_t addr = 2;
1062 int ret = i2c_smbus_read_byte_data(file, addr);
1063 if (ret < 0)
1064 {
1065 std::cerr << "Error " << __FUNCTION__ << "\n";
1066 return false;
1067 }
1068 val = static_cast<uint8_t>(ret);
1069 return true;
1070 }
1071
1072 bool getBootVer(uint8_t& val)
1073 {
1074 constexpr uint8_t addr = 3;
1075 int ret = i2c_smbus_read_byte_data(file, addr);
1076 if (ret < 0)
1077 {
1078 std::cerr << "Error " << __FUNCTION__ << "\n";
1079 return false;
1080 }
1081 val = static_cast<uint8_t>(ret);
1082 return true;
1083 }
1084
1085 bool getFPGAVer(uint8_t& val)
1086 {
1087 constexpr uint8_t addr = 4;
1088 int ret = i2c_smbus_read_byte_data(file, addr);
1089 if (ret < 0)
1090 {
1091 std::cerr << "Error " << __FUNCTION__ << "\n";
1092 return false;
1093 }
1094 val = static_cast<uint8_t>(ret);
1095 return true;
1096 }
1097
1098 bool getSecurityRev(uint8_t& val)
1099 {
1100 constexpr uint8_t addr = 5;
1101 int ret = i2c_smbus_read_byte_data(file, addr);
1102 if (ret < 0)
1103 {
1104 std::cerr << "Error " << __FUNCTION__ << "\n";
1105 return false;
1106 }
1107 val = static_cast<uint8_t>(ret);
1108 return true;
1109 }
1110
1111 bool getPresence(uint8_t& val)
1112 {
1113 // NVMe drives do not assert PRSNTn, and as such do not get reported as
1114 // PRESENT in this register
1115
1116 constexpr uint8_t addr = 8;
1117
1118 int ret = i2c_smbus_read_byte_data(file, addr);
1119 if (ret < 0)
1120 {
1121 std::cerr << "Error " << __FUNCTION__ << "\n";
1122 return false;
1123 }
1124 // presence is inverted
1125 val = static_cast<uint8_t>(~ret);
1126 return true;
1127 }
1128
1129 bool getIFDET(uint8_t& val)
1130 {
1131 // This register is a bitmap of parallel GPIO pins connected to the
1132 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
1133 // IFDETn low when they are inserted into the HSBP.This register, in
1134 // combination with the PRESENCE register, are used by the BMC to detect
1135 // the presence of NVMe drives.
1136
1137 constexpr uint8_t addr = 9;
1138
1139 int ret = i2c_smbus_read_byte_data(file, addr);
1140 if (ret < 0)
1141 {
1142 std::cerr << "Error " << __FUNCTION__ << "\n";
1143 return false;
1144 }
1145 // ifdet is inverted
1146 val = static_cast<uint8_t>(~ret);
1147 return true;
1148 }
1149
1150 bool getFailed(uint8_t& val)
1151 {
1152 constexpr uint8_t addr = 0xC;
1153 int ret = i2c_smbus_read_byte_data(file, addr);
1154 if (ret < 0)
1155 {
1156 std::cerr << "Error " << __FUNCTION__ << "\n";
1157 return false;
1158 }
1159 val = static_cast<uint8_t>(ret);
1160 return true;
1161 }
1162
1163 bool getRebuild(uint8_t& val)
1164 {
1165 constexpr uint8_t addr = 0xD;
1166 int ret = i2c_smbus_read_byte_data(file, addr);
1167 if (ret < 0)
1168 {
James Feistd86629c2020-04-23 10:07:25 -07001169 std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
1170 << "\n";
James Feist45772222019-09-27 10:38:08 -07001171 return false;
1172 }
1173 val = static_cast<uint8_t>(ret);
1174 return true;
1175 }
1176
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001177 bool getInsertedAndRemovedNvmeDrives(
1178 std::forward_list<std::string>& nvmeDrivesInserted,
1179 std::forward_list<std::string>& nvmeDrivesRemoved)
1180 {
1181 /* Get the current drives status */
1182 std::bitset<8> currDriveStatus;
1183 uint8_t nPresence;
1184 uint8_t nIfdet;
1185
1186 if (!getPresence(nPresence) || !getIFDET(nIfdet))
1187 {
1188 /* Error getting value. Return */
1189 std::cerr << "Backplane " << name
1190 << " failed to get drive status\n";
1191 return false;
1192 }
1193
1194 std::string dbusHsbpName = boost::replace_all_copy(name, " ", "_");
1195 auto nvmeMap = hsbpConfig.hsbpNvmeMap.find(dbusHsbpName);
1196 if (nvmeMap == hsbpConfig.hsbpNvmeMap.end())
1197 {
1198 std::cerr << "Couldn't get the NVMe Map for the backplane : "
1199 << name << "\n";
1200 return false;
1201 }
1202
1203 /* NVMe drives do not assert PRSNTn, and as such do not get reported in
1204 * "presence" register, but assert ifdet low. This implies for a NVMe
1205 * drive to be present, corresponding precense bit has to be 0 and idfet
1206 * has to be 1 (as the values of these regosters are negated: check
1207 * getPresence() and getIfdet() functions) */
1208 for (uint8_t bit = 0; bit < 8; bit++)
1209 {
1210 if ((nPresence & (1U << bit)) == 0)
1211 {
1212 if (nIfdet & (1U << bit))
1213 {
1214 currDriveStatus.set(bit);
1215 }
1216 }
1217 }
1218
1219 /* Determine Inserted and Removed Drives
1220 * Prev Bit | Curr Bit | Status
1221 * 0 | 0 | No Change
1222 * 0 | 1 | Inserted
1223 * 1 | 0 | Removed
1224 * 1 | 1 | No Change
1225 */
1226 for (uint8_t index = 0; index < 8; index++)
1227 {
1228 /* Inserted */
1229 if (!prevDriveStatus.test(index) && currDriveStatus.test(index))
1230 {
1231 nvmeDrivesInserted.emplace_front(nvmeMap->second.at(index));
1232 std::cerr << name << " : " << nvmeDrivesInserted.front()
1233 << " Inserted !\n";
1234 }
1235
1236 /* Removed */
1237 else if (prevDriveStatus.test(index) &&
1238 !currDriveStatus.test(index))
1239 {
1240 nvmeDrivesRemoved.emplace_front(nvmeMap->second.at(index));
1241 std::cerr << name << " : " << nvmeDrivesRemoved.front()
1242 << " Removed !\n";
1243 }
1244 }
1245
1246 prevDriveStatus = currDriveStatus;
1247 return true;
1248 }
1249
James Feist09dd2312019-10-09 09:29:03 -07001250 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -07001251 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001252 timer.cancel();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001253 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -07001254 objServer.remove_interface(versionIface);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001255 objServer.remove_interface(storageInterface);
1256 objServer.remove_interface(assetInterface);
Vikash Chandolaad11f7d2023-04-28 17:23:52 +05301257 objServer.remove_interface(activationIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001258 if (file >= 0)
1259 {
1260 close(file);
1261 }
1262 }
1263
1264 size_t bus;
1265 size_t address;
James Feist45772222019-09-27 10:38:08 -07001266 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001267 std::string name;
James Feistd86629c2020-04-23 10:07:25 -07001268 boost::asio::steady_timer timer;
James Feist45772222019-09-27 10:38:08 -07001269 bool present = false;
1270 uint8_t typeId = 0;
1271 uint8_t bootVer = 0;
1272 uint8_t fpgaVer = 0;
1273 uint8_t securityRev = 0;
1274 uint8_t funSupported = 0;
1275 uint8_t presence = 0;
1276 uint8_t ifdet = 0;
1277 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -07001278 uint8_t rebuilding = 0;
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001279 std::bitset<8> prevDriveStatus;
James Feist45772222019-09-27 10:38:08 -07001280
1281 int file = -1;
1282
Feist, Jamesc95cf672019-08-29 16:10:35 -07001283 std::string type;
1284
1285 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -07001286 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -08001287 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
1288 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
Vikash Chandolaad11f7d2023-04-28 17:23:52 +05301289 std::shared_ptr<sdbusplus::asio::dbus_interface> activationIface;
James Feist42b49c12019-10-29 15:18:43 -07001290 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -07001291 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -07001292 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001293};
1294
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001295/* Global HSBP backplanes and NVMe drives collection */
James Feistd86629c2020-04-23 10:07:25 -07001296std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
James Feist42b49c12019-10-29 15:18:43 -07001297std::list<Drive> ownerlessDrives; // drives without a backplane
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001298/***************************** End of Section *******************************/
Feist, Jamesc95cf672019-08-29 16:10:35 -07001299
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001300/****************************************************************************/
1301/***************** Miscellaneous Class/Function Definitions *****************/
1302/****************************************************************************/
1303/* The purpose of this class is to sync the code flow. Often times there could
1304 * be multiple dbus calls which are async, and upon completely finishing all
1305 * Dbus calls, we need to call next function, or handle the error.
1306 * When an object of this class goes out of scope, the respective handlers
1307 * will be called */
1308class AsyncCallbackHandler
1309{
1310 bool errorOccurred = false;
1311 std::function<void()> onSuccess = nullptr;
1312 std::function<void()> onError = nullptr;
1313
1314 public:
1315 explicit AsyncCallbackHandler(std::function<void()> onSuccessIn,
1316 std::function<void()> onErrorIn) :
1317 onSuccess(std::move(onSuccessIn)),
1318 onError(std::move(onErrorIn))
1319 {
1320 }
1321
1322 void setError()
1323 {
1324 errorOccurred = true;
1325 }
1326
1327 ~AsyncCallbackHandler()
1328 {
1329 /* If error occurred flag was set, execute the error handler */
1330 if (errorOccurred)
1331 {
1332 /* Check if Error Handler is defined */
1333 if (onError)
1334 {
1335 onError();
1336 }
1337
1338 return;
1339 }
1340
1341 /* If Success Handler is present, execute Success Handler */
1342 if (onSuccess)
1343 {
1344 onSuccess();
1345 }
1346 }
1347};
1348
1349void stopHsbpManager()
1350{
1351 std::cerr << __FUNCTION__ << ": Stopping hsbp-manager\n";
1352 appState = AppState::idle;
1353 hsbpConfig.clearConfig();
1354 clockBuffers.clear();
1355 ioExpanders.clear();
1356 backplanes.clear();
1357
1358 io.stop();
1359}
1360/***************************** End of Section *******************************/
1361
1362/****************************************************************************/
1363/********* HSBP clock enable/disable related Function Definitions ***********/
1364/****************************************************************************/
1365void updateHsbpClocks(std::forward_list<std::string>& nvmeDrivesInserted,
1366 std::forward_list<std::string>& nvmeDrivesRemoved)
1367{
1368 if (appState < AppState::backplanesLoaded)
1369 {
1370 std::cerr << "HSBP not initialized ! Cancelling Clock Update ! \n";
1371 return;
1372 }
1373
1374 std::cerr << "Updating HSBP drive clocks ...\n";
1375
1376 /* Loop through all clock buffers and try to update the clocks (this will be
1377 * done if the mode of operation of the clock buffer is SMBus) */
1378 for (auto& clockBuffer : clockBuffers)
1379 {
1380 if (!clockBuffer.enableDisableClock(nvmeDrivesInserted,
1381 nvmeDrivesRemoved))
1382 {
1383 std::cerr << "Error Occurred while setting the clock in \""
1384 << clockBuffer.getName() << "\"\n";
1385 }
1386 }
1387
1388 /* If there are drives yet to be updated, check all the IO Expanders in case
1389 * they are mapped to the drives and enable the respective IO */
1390 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1391 {
1392 for (auto& ioExpander : ioExpanders)
1393 {
1394 if (!ioExpander.enableDisableOuput(nvmeDrivesInserted,
1395 nvmeDrivesRemoved))
1396 {
1397 std::cerr << "Error Occurred while setting the IO in \""
1398 << ioExpander.getName() << "\"\n";
1399 }
1400 }
1401 }
1402
1403 /* If there are drives still left, then one or more drives clock
1404 * enable/diable failed. There is a possibility of improper mapping or
1405 * current communication with the device failed */
1406 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1407 {
1408 std::cerr << "Critical Error !!!\nMapping issue detected !\n";
1409
1410 if (!nvmeDrivesInserted.empty())
1411 {
1412 std::cerr << "The clock enable failed for : ";
1413 for (auto& nvme1 : nvmeDrivesInserted)
1414 {
1415 std::cerr << nvme1 << ", ";
1416 }
1417 std::cerr << "\n";
1418 }
1419
1420 if (!nvmeDrivesRemoved.empty())
1421 {
1422 std::cerr << "The clock disable failed for : ";
1423 for (auto& nvme1 : nvmeDrivesRemoved)
1424 {
1425 std::cerr << nvme1 << ", ";
1426 }
1427 std::cerr << "\n";
1428 }
1429 }
1430}
1431
1432void scanHsbpDrives(bool& hsbpDriveScanInProgress)
1433{
1434 std::cerr << __FUNCTION__ << ": Scanning HSBP drives status ...\n";
1435
1436 /* List variables to store the drives Inserted/Removed */
1437 std::forward_list<std::string> nvmeDrivesInserted;
1438 std::forward_list<std::string> nvmeDrivesRemoved;
1439
1440 /* Loop through each backplane present and get the list of inserted/removed
1441 * drives */
1442 for (auto& [name, backplane] : backplanes)
1443 {
1444 backplane->getInsertedAndRemovedNvmeDrives(nvmeDrivesInserted,
1445 nvmeDrivesRemoved);
1446 }
1447
1448 if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1449 {
1450 updateHsbpClocks(nvmeDrivesInserted, nvmeDrivesRemoved);
1451 }
1452
1453 std::cerr << __FUNCTION__ << ": Scanning HSBP drives Completed\n";
1454
1455 hsbpDriveScanInProgress = false;
1456}
1457
1458void checkHsbpDrivesStatus()
1459{
1460 static bool hsbpDriveScanInProgress = false;
1461 static bool hsbpDriveRescanInQueue = false;
1462
1463 if (appState < AppState::backplanesLoaded)
1464 {
1465 std::cerr << __FUNCTION__
1466 << ": HSBP not initialized ! Cancelling scan of HSBP drives "
1467 "status ! \n";
1468 return;
1469 }
1470
1471 if (hsbpDriveScanInProgress)
1472 {
1473 /* Scan and Clock Update already in progress. Try again after sometime.
1474 * This event can occur due to the GPIO interrupt */
1475 std::cerr << __FUNCTION__
1476 << ": HSBP Drives Scan is already in progress\n";
1477 if (hsbpDriveRescanInQueue)
1478 {
1479 /* There is already a Re-Scan in queue. No need to create multiple
1480 * rescans */
1481 return;
1482 }
1483
1484 hsbpDriveRescanInQueue = true;
1485
1486 std::cerr << __FUNCTION__ << ": Queuing the Scan \n";
1487
1488 auto driveScanTimer = std::make_shared<boost::asio::steady_timer>(io);
1489 driveScanTimer->expires_after(std::chrono::seconds(1));
1490 driveScanTimer->async_wait(
1491 [driveScanTimer](const boost::system::error_code ec) {
1492 if (ec == boost::asio::error::operation_aborted)
1493 {
1494 // Timer was Aborted
1495 return;
1496 }
1497 else if (ec)
1498 {
1499 std::cerr << "driveScanTimer: Timer error" << ec.message()
1500 << "\n";
1501 return;
1502 }
1503 hsbpDriveRescanInQueue = false;
1504 checkHsbpDrivesStatus();
1505 });
1506
1507 return;
1508 }
1509
1510 hsbpDriveScanInProgress = true;
1511
1512 /* Post the scan to IO queue and return from here. This enables capturing
1513 * next GPIO event if any */
1514 boost::asio::post(io, []() { scanHsbpDrives(hsbpDriveScanInProgress); });
1515}
1516/***************************** End of Section *******************************/
1517
1518/****************************************************************************/
1519/********** Backplanes and NVMe drives related Function Definitions *********/
1520/****************************************************************************/
James Feist8675a912019-10-16 14:36:58 -07001521static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -07001522{
James Feist8675a912019-10-16 14:36:58 -07001523 size_t count = 0;
1524 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -07001525 {
James Feistd86629c2020-04-23 10:07:25 -07001526 count += backplane->drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -07001527 }
James Feist8675a912019-10-16 14:36:58 -07001528 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -07001529}
1530
James Feist8675a912019-10-16 14:36:58 -07001531void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -07001532{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001533 appState = AppState::loadingDrives;
1534
1535 /* Setup a callback to be called once the assets are populated completely or
1536 * fallback to error handler */
1537 auto drivesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1538 []() {
1539 appState = AppState::drivesLoaded;
1540 std::cerr << "Drives Updated !\n";
1541 },
1542 []() {
1543 // TODO: Handle this error if needed
1544 appState = AppState::backplanesLoaded;
1545 std::cerr << "An error occured ! Drives load failed \n";
1546 });
James Feist0b236ab2019-10-02 09:09:16 -07001547
1548 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001549 [drivesLoadedCallback](const boost::system::error_code ec,
1550 const GetSubTreeType& subtree) {
James Feist0b236ab2019-10-02 09:09:16 -07001551 if (ec)
1552 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001553 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1554 << ec.message() << "\n";
1555 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001556 return;
1557 }
James Feist8675a912019-10-16 14:36:58 -07001558
1559 // drives may get an owner during this, or we might disover more
1560 // drives
1561 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -07001562 for (const auto& [path, objDict] : subtree)
1563 {
1564 if (objDict.empty())
1565 {
1566 continue;
1567 }
1568
1569 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -07001570 // we export this interface too
1571 if (owner == busName)
1572 {
1573 continue;
1574 }
James Feist8675a912019-10-16 14:36:58 -07001575 if (std::find(objDict.begin()->second.begin(),
1576 objDict.begin()->second.end(),
1577 assetTag) == objDict.begin()->second.end())
1578 {
1579 // no asset tag to associate to
1580 continue;
1581 }
1582
James Feist0b236ab2019-10-02 09:09:16 -07001583 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001584 [path, drivesLoadedCallback](
1585 const boost::system::error_code ec2,
1586 const boost::container::flat_map<
1587 std::string, std::variant<uint64_t, std::string>>&
1588 values) {
James Feist0b236ab2019-10-02 09:09:16 -07001589 if (ec2)
1590 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001591 std::cerr << __FUNCTION__
1592 << ": Error Getting Config "
1593 << ec2.message() << " "
James Feist0b236ab2019-10-02 09:09:16 -07001594 << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001595 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001596 return;
1597 }
1598 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -07001599
James Feist8675a912019-10-16 14:36:58 -07001600 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -07001601 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001602 std::cerr << __FUNCTION__
1603 << ": Illegal interface at " << path
James Feist0b236ab2019-10-02 09:09:16 -07001604 << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001605 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001606 return;
1607 }
1608
James Feist8675a912019-10-16 14:36:58 -07001609 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -07001610 size_t muxBus = static_cast<size_t>(
1611 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -07001612 std::filesystem::path muxPath =
1613 "/sys/bus/i2c/devices/i2c-" +
1614 std::to_string(muxBus) + "/mux_device";
1615 if (!std::filesystem::is_symlink(muxPath))
1616 {
1617 std::cerr << path << " mux does not exist\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001618 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001619 return;
1620 }
1621
James Feist8675a912019-10-16 14:36:58 -07001622 // we should be getting something of the form 7-0052
1623 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -07001624 std::string fname =
1625 std::filesystem::read_symlink(muxPath).filename();
1626 auto findDash = fname.find('-');
1627
1628 if (findDash == std::string::npos ||
1629 findDash + 1 >= fname.size())
1630 {
1631 std::cerr << path << " mux path invalid\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001632 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001633 return;
1634 }
1635
1636 std::string busStr = fname.substr(0, findDash);
1637 std::string muxStr = fname.substr(findDash + 1);
1638
1639 size_t bus = static_cast<size_t>(std::stoi(busStr));
1640 size_t addr =
1641 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -07001642 size_t muxIndex = 0;
1643
1644 // find the channel of the mux the drive is on
1645 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
1646 std::to_string(muxBus) +
1647 "/name");
1648 if (!nameFile)
1649 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001650 std::cerr << __FUNCTION__
1651 << ": Unable to open name file of bus "
James Feist8675a912019-10-16 14:36:58 -07001652 << muxBus << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001653 drivesLoadedCallback->setError();
James Feist8675a912019-10-16 14:36:58 -07001654 return;
1655 }
1656
1657 std::string nameStr;
1658 std::getline(nameFile, nameStr);
1659
1660 // file is of the form "i2c-4-mux (chan_id 1)", get chan
1661 // assume single digit chan
1662 const std::string prefix = "chan_id ";
1663 size_t findId = nameStr.find(prefix);
1664 if (findId == std::string::npos ||
1665 findId + 1 >= nameStr.size())
1666 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001667 std::cerr << __FUNCTION__
1668 << ": Illegal name file on bus " << muxBus
James Feist8675a912019-10-16 14:36:58 -07001669 << "\n";
1670 }
1671
1672 std::string indexStr =
1673 nameStr.substr(findId + prefix.size(), 1);
1674
1675 size_t driveIndex = std::stoi(indexStr);
1676
James Feist8675a912019-10-16 14:36:58 -07001677 boost::container::flat_map<std::string, std::string>
1678 assetInventory;
1679 const std::array<const char*, 4> assetKeys = {
1680 "PartNumber", "SerialNumber", "Manufacturer",
1681 "Model"};
1682 for (const auto& [key, value] : values)
1683 {
1684 if (std::find(assetKeys.begin(), assetKeys.end(),
1685 key) == assetKeys.end())
1686 {
1687 continue;
1688 }
1689 assetInventory[key] = std::get<std::string>(value);
1690 }
1691
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001692 Backplane* parent = nullptr;
1693 for (auto& [name, backplane] : backplanes)
1694 {
1695 muxIndex = 0;
1696 for (const Mux& mux : *(backplane->muxes))
1697 {
1698 if (bus == mux.bus && addr == mux.address)
1699 {
1700 parent = backplane.get();
1701 break;
1702 }
1703 muxIndex += mux.channels;
1704 }
1705 if (parent)
1706 {
1707 /* Found the backplane. No need to proceed
1708 * further */
1709 break;
1710 }
1711 }
1712
James Feist8675a912019-10-16 14:36:58 -07001713 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -07001714 if (parent == nullptr)
1715 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001716 std::string driveName =
1717 "Drive_" + std::to_string(getDriveCount() + 1);
James Feist8675a912019-10-16 14:36:58 -07001718 auto& drive = ownerlessDrives.emplace_back(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001719 driveName, true, true, true, false);
James Feist8675a912019-10-16 14:36:58 -07001720 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -07001721 return;
1722 }
James Feist8675a912019-10-16 14:36:58 -07001723
1724 driveIndex += muxIndex;
1725
James Feist0b236ab2019-10-02 09:09:16 -07001726 if (parent->drives.size() <= driveIndex)
1727 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001728 std::cerr << __FUNCTION__
1729 << ": Illegal drive index at " << path
James Feist0b236ab2019-10-02 09:09:16 -07001730 << " " << driveIndex << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001731 drivesLoadedCallback->setError();
James Feist0b236ab2019-10-02 09:09:16 -07001732 return;
1733 }
James Feist42b49c12019-10-29 15:18:43 -07001734 auto it = parent->drives.begin();
1735 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -07001736
James Feist42b49c12019-10-29 15:18:43 -07001737 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -07001738 },
1739 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -07001740 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -07001741 }
1742 },
1743 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001744 0, std::array<const char*, 1>{nvmeIntf});
James Feist0b236ab2019-10-02 09:09:16 -07001745}
1746
James Feist8675a912019-10-16 14:36:58 -07001747void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -07001748 std::string& rootPath)
1749{
1750 const static std::array<const std::string, 4> muxTypes = {
1751 "xyz.openbmc_project.Configuration.PCA9543Mux",
1752 "xyz.openbmc_project.Configuration.PCA9544Mux",
1753 "xyz.openbmc_project.Configuration.PCA9545Mux",
1754 "xyz.openbmc_project.Configuration.PCA9546Mux"};
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001755
James Feist0b236ab2019-10-02 09:09:16 -07001756 conn->async_method_call(
1757 [muxes](const boost::system::error_code ec,
1758 const GetSubTreeType& subtree) {
1759 if (ec)
1760 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001761 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1762 << ec.message() << "\n";
James Feist0b236ab2019-10-02 09:09:16 -07001763 return;
1764 }
James Feist8675a912019-10-16 14:36:58 -07001765 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -07001766 for (const auto& [path, objDict] : subtree)
1767 {
1768 if (objDict.empty() || objDict.begin()->second.empty())
1769 {
1770 continue;
1771 }
1772
1773 const std::string& owner = objDict.begin()->first;
1774 const std::vector<std::string>& interfaces =
1775 objDict.begin()->second;
1776
1777 const std::string* interface = nullptr;
1778 for (const std::string& iface : interfaces)
1779 {
1780 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
1781 muxTypes.end())
1782 {
1783 interface = &iface;
1784 break;
1785 }
1786 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001787
James Feist0b236ab2019-10-02 09:09:16 -07001788 if (interface == nullptr)
1789 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001790 std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
James Feist0b236ab2019-10-02 09:09:16 -07001791 continue;
1792 }
1793
1794 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001795 [path, muxes, index](
James Feist0b236ab2019-10-02 09:09:16 -07001796 const boost::system::error_code ec2,
1797 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -07001798 std::string,
1799 std::variant<uint64_t, std::vector<std::string>>>&
1800 values) {
James Feist0b236ab2019-10-02 09:09:16 -07001801 if (ec2)
1802 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001803 std::cerr << __FUNCTION__
1804 << ": Error Getting Config "
1805 << ec2.message() << "\n";
James Feist0b236ab2019-10-02 09:09:16 -07001806 return;
1807 }
1808 auto findBus = values.find("Bus");
1809 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -07001810 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -07001811 if (findBus == values.end() ||
1812 findAddress == values.end())
1813 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001814 std::cerr << __FUNCTION__
1815 << ": Illegal configuration at " << path
James Feist0b236ab2019-10-02 09:09:16 -07001816 << "\n";
1817 return;
1818 }
1819 size_t bus = static_cast<size_t>(
1820 std::get<uint64_t>(findBus->second));
1821 size_t address = static_cast<size_t>(
1822 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -07001823 std::vector<std::string> channels =
1824 std::get<std::vector<std::string>>(
1825 findChannelNames->second);
1826 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -07001827 },
1828 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1829 *interface);
James Feist8675a912019-10-16 14:36:58 -07001830 index++;
James Feist0b236ab2019-10-02 09:09:16 -07001831 }
1832 },
1833 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
1834 rootPath, 1, muxTypes);
1835}
1836
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001837void populateHsbpBackplanes(
1838 const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001839{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001840 std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
1841 appState = AppState::loadingBackplanes;
Jayaprakash Mutyala0c5059f2021-11-10 22:09:55 +00001842 backplanes.clear();
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001843
Feist, Jamesc95cf672019-08-29 16:10:35 -07001844 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001845 [backplanesLoadedCallback](const boost::system::error_code ec,
1846 const GetSubTreeType& subtree) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001847 if (ec)
1848 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001849 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1850 << ec.message() << "\n";
1851 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001852 return;
1853 }
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001854
1855 if (subtree.empty())
1856 {
1857 /* There wer no HSBP's detected. set teh state back to
1858 * componentsLoaded so that on backplane match event, the
1859 * process can start again */
1860 appState = AppState::componentsLoaded;
1861 std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
1862 return;
1863 }
1864
Feist, Jamesc95cf672019-08-29 16:10:35 -07001865 for (const auto& [path, objDict] : subtree)
1866 {
1867 if (objDict.empty())
1868 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001869 std::cerr << __FUNCTION__
1870 << ": Subtree data "
1871 "corrupted !\n";
1872 backplanesLoadedCallback->setError();
1873 return;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001874 }
1875
1876 const std::string& owner = objDict.begin()->first;
1877 conn->async_method_call(
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001878 [backplanesLoadedCallback, path,
1879 owner](const boost::system::error_code ec2,
1880 const boost::container::flat_map<
1881 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001882 if (ec2)
1883 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001884 std::cerr << __FUNCTION__
1885 << ": Error Getting Config "
Feist, Jamesc95cf672019-08-29 16:10:35 -07001886 << ec2.message() << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001887 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001888 return;
1889 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001890 std::optional<size_t> bus;
1891 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -07001892 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001893 std::optional<std::string> name;
1894 for (const auto& [key, value] : resp)
1895 {
1896 if (key == "Bus")
1897 {
1898 bus = std::get<uint64_t>(value);
1899 }
1900 else if (key == "Address")
1901 {
1902 address = std::get<uint64_t>(value);
1903 }
James Feist45772222019-09-27 10:38:08 -07001904 else if (key == "Index")
1905 {
1906 backplaneIndex = std::get<uint64_t>(value);
1907 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001908 else if (key == "Name")
1909 {
1910 name = std::get<std::string>(value);
1911 }
1912 }
James Feist45772222019-09-27 10:38:08 -07001913 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001914 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001915 std::cerr << __FUNCTION__
1916 << ": Illegal configuration at " << path
Feist, Jamesc95cf672019-08-29 16:10:35 -07001917 << "\n";
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001918 backplanesLoadedCallback->setError();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001919 return;
1920 }
James Feist0b236ab2019-10-02 09:09:16 -07001921 std::string parentPath =
1922 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001923 const auto& [backplane, status] = backplanes.emplace(
James Feistd86629c2020-04-23 10:07:25 -07001924 *name, std::make_shared<Backplane>(
1925 *bus, *address, *backplaneIndex, *name));
1926 backplane->second->run(parentPath, owner);
1927 populateMuxes(backplane->second->muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001928 },
1929 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001930 hsbpCpldInft);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001931 }
1932 },
1933 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08001934 0, std::array<const char*, 1>{hsbpCpldInft});
1935}
1936
1937void setUpBackplanesAndDrives()
1938{
1939 static bool backplanesScanInProgress = false;
1940 static bool backplanesRescanInQueue = false;
1941
1942 if (appState < AppState::componentsLoaded)
1943 {
1944 std::cerr << __FUNCTION__
1945 << ": Components are not initialized ! Cancelling scan of "
1946 "Backplanes ! \n";
1947 return;
1948 }
1949
1950 if (backplanesScanInProgress)
1951 {
1952 std::cerr << __FUNCTION__
1953 << ": Backplanes Scan is already in progress\n";
1954 if (backplanesRescanInQueue)
1955 {
1956 /* There is already a Re-Scan in queue. No need to create multiple
1957 * rescans */
1958 return;
1959 }
1960
1961 backplanesRescanInQueue = true;
1962
1963 std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
1964
1965 auto backplaneScanTimer =
1966 std::make_shared<boost::asio::steady_timer>(io);
1967 backplaneScanTimer->expires_after(std::chrono::seconds(1));
1968 backplaneScanTimer->async_wait(
1969 [backplaneScanTimer](const boost::system::error_code ec) {
1970 if (ec == boost::asio::error::operation_aborted)
1971 {
1972 // Timer was Aborted
1973 return;
1974 }
1975 else if (ec)
1976 {
1977 std::cerr << "backplaneScanTimer: Timer error"
1978 << ec.message() << "\n";
1979 return;
1980 }
1981 backplanesRescanInQueue = false;
1982 setUpBackplanesAndDrives();
1983 });
1984
1985 return;
1986 }
1987
1988 backplanesScanInProgress = true;
1989
1990 /* Set Callback to be called once backplanes are populated to call
1991 * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
1992 auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1993 []() {
1994 /* If no HSBP's were detected, the state changes to
1995 * componentsLoaded. Proceed further only if state was
1996 * loadingBackplanes */
1997 if (appState != AppState::loadingBackplanes)
1998 {
1999 backplanesScanInProgress = false;
2000 return;
2001 }
2002
2003 /* If there is a ReScan in the Queue, dont proceed further. Load the
2004 * Backplanes again and then proceed further */
2005 if (backplanesRescanInQueue)
2006 {
2007 backplanesScanInProgress = false;
2008 return;
2009 }
2010
2011 appState = AppState::backplanesLoaded;
2012 std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
2013
2014 checkHsbpDrivesStatus();
2015 updateAssets();
2016 backplanesScanInProgress = false;
2017 },
2018 []() {
2019 /* Loading Backplanes is an important step. If the load failed due
2020 * to an error, stop the app so that restart cant be triggerred */
2021 std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
2022 appState = AppState::idle;
2023 backplanesScanInProgress = false;
2024 stopHsbpManager();
2025 });
2026
2027 populateHsbpBackplanes(backplanesLoadedCallback);
2028}
2029
2030void setupBackplanesAndDrivesMatch()
2031{
2032 static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
2033 *conn,
2034 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2035 "member='PropertiesChanged', "
2036 "interface='org.freedesktop.DBus.Properties', "
2037 "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2038 std::string(hsbpCpldInft) + "'",
2039 [](sdbusplus::message_t& msg) {
2040 std::string intfName;
2041 boost::container::flat_map<std::string, BasicVariantType> values;
2042 msg.read(intfName, values);
2043
2044 /* This match will be triggered for each of the property being set
2045 * under the hsbpCpldInft interface. Call the loader only on one
2046 * property say "name". This will avoid multiple calls to populate
2047 * function
2048 */
2049 for (const auto& [key, value] : values)
2050 {
2051 if (key == "Name")
2052 {
2053 /* This match will be triggered when ever there is a
2054 * addition/removal of HSBP backplane. At this stage, all
2055 * the HSBP's need to be populated again and also assets
2056 * have to be re-discovered. So, setting state to
2057 * componentsLoaded and calling setUpBackplanesAndDrives()
2058 * only if configuration and components loading was
2059 * completed */
2060 if (appState < AppState::componentsLoaded)
2061 {
2062 /* Configuration is not loaded yet. Backplanes will be
2063 * loaded
2064 * once configuration and components are loaded. */
2065 std::cerr << __FUNCTION__
2066 << ": Discarding Backplane match\n";
2067 return;
2068 }
2069
2070 appState = AppState::componentsLoaded;
2071
2072 /* We will call the function after a small delay to let all
2073 * the properties to be intialized */
2074 auto backplaneTimer =
2075 std::make_shared<boost::asio::steady_timer>(io);
2076 backplaneTimer->expires_after(std::chrono::seconds(2));
2077 backplaneTimer->async_wait(
2078 [backplaneTimer](const boost::system::error_code ec) {
2079 if (ec == boost::asio::error::operation_aborted)
2080 {
2081 return;
2082 }
2083 else if (ec)
2084 {
2085 std::cerr << "backplaneTimer: Timer error"
2086 << ec.message() << "\n";
2087 return;
2088 }
2089 setUpBackplanesAndDrives();
2090 });
2091 }
2092 }
2093 });
2094
2095 static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
2096 *conn,
2097 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2098 "member='PropertiesChanged', "
2099 "interface='org.freedesktop.DBus.Properties', arg0='" +
2100 std::string(nvmeIntf) + "'",
2101 [](sdbusplus::message_t& msg) {
2102 std::string intfName;
2103 boost::container::flat_map<std::string, BasicVariantType> values;
2104 msg.read(intfName, values);
2105
2106 /* This match will be triggered for each of the property being set
2107 * under the nvmeIntf interface. Call the loader only on one
2108 * property say "name". This will avoid multiple calls to populate
2109 * function
2110 */
2111 for (const auto& [key, value] : values)
2112 {
2113 if (key == "Name")
2114 {
2115 /* This match will be triggered when ever there is a
2116 * addition/removal of drives. At this stage only assets
2117 * have to be re-discovered. So, setting state to
2118 * backplanesLoaded and calling updateAssets() only if all
2119 * previous states are completed */
2120 if (appState < AppState::backplanesLoaded)
2121 {
2122 /* Configuration is not loaded yet. Drives will be
2123 * loaded once
2124 * configuration, components and backplanes are loaded.
2125 */
2126 std::cerr << __FUNCTION__
2127 << ": Discarding Drive match\n";
2128 return;
2129 }
2130
2131 appState = AppState::backplanesLoaded;
2132
2133 /* We will call the function after a small delay to let all
2134 * the properties to be intialized */
2135 auto driveTimer =
2136 std::make_shared<boost::asio::steady_timer>(io);
2137 driveTimer->expires_after(std::chrono::seconds(2));
2138 driveTimer->async_wait(
2139 [driveTimer](const boost::system::error_code ec) {
2140 if (ec == boost::asio::error::operation_aborted)
2141 {
2142 return;
2143 }
2144 else if (ec)
2145 {
2146 std::cerr << "driveTimer: Timer error"
2147 << ec.message() << "\n";
2148 return;
2149 }
2150 updateAssets();
2151 });
2152 }
2153 }
2154 });
2155}
2156/***************************** End of Section *******************************/
2157
2158/****************************************************************************/
2159/******************* Components related Function Definitions ****************/
2160/****************************************************************************/
2161bool verifyComponentsLoaded()
2162{
2163 std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
2164
2165 /* Loop through all clock buffers */
2166 for (auto& clockBuffer : clockBuffers)
2167 {
2168 if (!clockBuffer.isInitialized())
2169 {
2170 std::cerr << "Critical Error: Initializing \""
2171 << clockBuffer.getName() << "\" failed\n";
2172 return false;
2173 }
2174 }
2175
2176 /* Loop through all IO Expanders */
2177 for (auto& ioExpander : ioExpanders)
2178 {
2179 if (!ioExpander.isInitialized())
2180 {
2181 std::cerr << "Critical Error: Initializing \""
2182 << ioExpander.getName() << "\" failed\n";
2183 return false;
2184 }
2185 }
2186
2187 std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
2188
2189 return true;
2190}
2191/***************************** End of Section *******************************/
2192
2193/****************************************************************************/
2194/****************** IO expander related Function Definitions ****************/
2195/****************************************************************************/
2196void loadIoExpanderInfo(
2197 const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2198{
2199 appState = AppState::loadingComponents;
2200
2201 /* Clear global ioExpanders to start off */
2202 ioExpanders.clear();
2203
2204 conn->async_method_call(
2205 [componentsLoadedCallback](const boost::system::error_code ec,
2206 const GetSubTreeType& subtree) {
2207 if (ec)
2208 {
2209 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2210 << ec.message() << "\n";
2211 componentsLoadedCallback->setError();
2212 return;
2213 }
2214
2215 for (auto& [path, objDict] : subtree)
2216 {
2217
2218 if (objDict.empty())
2219 {
2220 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2221 componentsLoadedCallback->setError();
2222 return;
2223 }
2224
2225 /* Ideally there would be only one element in objDict as only
2226 * one service exposes it and there would be only one interface
2227 * so it is safe to directly read them without loop */
2228 const std::string& service = objDict.begin()->first;
2229 const std::string& intf = objDict.begin()->second.front();
2230
2231 conn->async_method_call(
2232 [componentsLoadedCallback](
2233 const boost::system::error_code er,
2234 const boost::container::flat_map<
2235 std::string, BasicVariantType>& resp) {
2236 if (er)
2237 {
2238 std::cerr << __FUNCTION__
2239 << ": Error Getting "
2240 "Config "
2241 << er.message() << "\n";
2242 componentsLoadedCallback->setError();
2243 return;
2244 }
2245
2246 std::optional<uint64_t> bus;
2247 std::optional<uint64_t> address;
2248 std::optional<uint64_t> confIORegAddr;
2249 std::optional<uint64_t> outCtrlBaseAddr;
2250 std::optional<uint64_t> outCtrlByteCount;
2251 std::unordered_map<std::string,
2252 std::vector<std::string>>
2253 ioMap;
2254 std::optional<std::string> name;
2255 std::optional<std::string> type;
2256
2257 /* Loop through to get all IO Expander properties */
2258 for (const auto& [key, value] : resp)
2259 {
2260 if (key == "Bus")
2261 {
2262 bus = std::get<uint64_t>(value);
2263 }
2264 else if (key == "Address")
2265 {
2266 address = std::get<uint64_t>(value);
2267 }
2268 else if (key == "ConfIORegAddr")
2269 {
2270 confIORegAddr = std::get<uint64_t>(value);
2271 }
2272 else if (key == "OutCtrlBaseAddr")
2273 {
2274 outCtrlBaseAddr = std::get<uint64_t>(value);
2275 }
2276 else if (key == "OutCtrlByteCount")
2277 {
2278 outCtrlByteCount = std::get<uint64_t>(value);
2279 }
2280 else if (key == "Name")
2281 {
2282 name = std::get<std::string>(value);
2283 }
2284 else if (key == "Type")
2285 {
2286 type = std::get<std::string>(value);
2287 }
2288 else if (key.starts_with("IO"))
2289 {
2290 std::optional<std::vector<std::string>> outList;
2291 outList = std::get<NvmeMapping>(value);
2292 if (!outList)
2293 {
2294 break;
2295 }
2296 ioMap.try_emplace(key, *outList);
2297 }
2298 }
2299
2300 /* Verify if all properties were defined */
2301 if (!bus || !address || !confIORegAddr ||
2302 !outCtrlBaseAddr || !outCtrlByteCount || !name)
2303 {
2304 std::cerr << __FUNCTION__
2305 << ": Incomplete "
2306 "Clock Buffer definition !! \n";
2307 componentsLoadedCallback->setError();
2308 return;
2309 }
2310
2311 /* Check if we were able to get byteMap correctly */
2312 if ((*outCtrlByteCount) != ioMap.size())
2313 {
2314 std::cerr << "loadIoExpanderInfo(): Incomplete "
2315 "IO Map !! \n";
2316 componentsLoadedCallback->setError();
2317 return;
2318 }
2319
2320 /* Create IO expander object and add it to global
2321 * ioExpanders vector */
2322 ioExpanders.emplace_front(
2323 *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
2324 *outCtrlByteCount, ioMap, *name, *type);
2325 },
2326 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2327 intf);
2328 }
2329 },
2330 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2331 0, hsbpConfig.ioExpanderTypes);
2332}
2333/***************************** End of Section *******************************/
2334
2335/****************************************************************************/
2336/***************** Clock buffer related Function Definitions ****************/
2337/****************************************************************************/
2338void loadClockBufferInfo(
2339 const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2340{
2341 appState = AppState::loadingComponents;
2342
2343 /* Clear global clockBuffers to start off */
2344 clockBuffers.clear();
2345
2346 conn->async_method_call(
2347 [componentsLoadedCallback](const boost::system::error_code ec,
2348 const GetSubTreeType& subtree) {
2349 if (ec)
2350 {
2351 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2352 << ec.message() << "\n";
2353 componentsLoadedCallback->setError();
2354 return;
2355 }
2356
2357 for (auto& [path, objDict] : subtree)
2358 {
2359
2360 if (objDict.empty())
2361 {
2362 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2363 componentsLoadedCallback->setError();
2364 return;
2365 }
2366
2367 /* Ideally there would be only one element in objDict as only
2368 * one service exposes it and there would be only one interface
2369 * so it is safe to directly read them without loop */
2370 const std::string& service = objDict.begin()->first;
2371 const std::string& intf = objDict.begin()->second.front();
2372
2373 conn->async_method_call(
2374 [componentsLoadedCallback](
2375 const boost::system::error_code er,
2376 const boost::container::flat_map<
2377 std::string, BasicVariantType>& resp) {
2378 if (er)
2379 {
2380 std::cerr << __FUNCTION__
2381 << ": Error Getting "
2382 "Config "
2383 << er.message() << "\n";
2384 componentsLoadedCallback->setError();
2385 return;
2386 }
2387
2388 std::optional<uint64_t> bus;
2389 std::optional<uint64_t> address;
2390 std::optional<std::string> mode;
2391 std::optional<uint64_t> outCtrlBaseAddr;
2392 std::optional<uint64_t> outCtrlByteCount;
2393 std::unordered_map<std::string,
2394 std::vector<std::string>>
2395 byteMap;
2396 std::optional<std::string> name;
2397 std::optional<std::string> type;
2398
2399 /* Loop through to get all Clock Buffer properties */
2400 for (const auto& [key, value] : resp)
2401 {
2402 if (key == "Bus")
2403 {
2404 bus = std::get<uint64_t>(value);
2405 }
2406 else if (key == "Address")
2407 {
2408 address = std::get<uint64_t>(value);
2409 }
2410 else if (key == "Mode")
2411 {
2412 mode = std::get<std::string>(value);
2413 }
2414 else if (key == "OutCtrlBaseAddr")
2415 {
2416 outCtrlBaseAddr = std::get<uint64_t>(value);
2417 }
2418 else if (key == "OutCtrlByteCount")
2419 {
2420 outCtrlByteCount = std::get<uint64_t>(value);
2421 }
2422 else if (key == "Name")
2423 {
2424 name = std::get<std::string>(value);
2425 }
2426 else if (key == "Type")
2427 {
2428 type = std::get<std::string>(value);
2429 }
2430 else if (key.starts_with("Byte"))
2431 {
2432 std::optional<std::vector<std::string>>
2433 byteList;
2434 byteList = std::get<NvmeMapping>(value);
2435 if (!byteList)
2436 {
2437 break;
2438 }
2439 byteMap.try_emplace(key, *byteList);
2440 }
2441 }
2442
2443 /* Verify if all properties were defined */
2444 if (!bus || !address || !mode || !outCtrlBaseAddr ||
2445 !outCtrlByteCount || !name)
2446 {
2447 std::cerr << __FUNCTION__
2448 << ": Incomplete "
2449 "Clock Buffer definition !! \n";
2450 componentsLoadedCallback->setError();
2451 return;
2452 }
2453
2454 /* Check if we were able to get byteMap correctly */
2455 if ((*outCtrlByteCount) != byteMap.size())
2456 {
2457 std::cerr << __FUNCTION__
2458 << ": Incomplete "
2459 "Byte Map !! \n";
2460 componentsLoadedCallback->setError();
2461 return;
2462 }
2463
2464 /* Create clock buffer object and add it to global
2465 * clockBuffers vector */
2466 clockBuffers.emplace_front(
2467 *bus, *address, *mode, *outCtrlBaseAddr,
2468 *outCtrlByteCount, byteMap, *name, *type);
2469 },
2470 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2471 intf);
2472 }
2473 },
2474 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2475 0, hsbpConfig.clockBufferTypes);
2476}
2477/***************************** End of Section *******************************/
2478
2479/****************************************************************************/
2480/***************** HSBP Config related Function Definitions *****************/
2481/****************************************************************************/
2482void loadHsbpConfig()
2483{
2484 appState = AppState::loadingHsbpConfig;
2485
2486 conn->async_method_call(
2487 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
2488 if (ec)
2489 {
2490 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2491 << ec.message() << "\n";
2492 return;
2493 }
2494
2495 if (subtree.empty())
2496 {
2497 /* Entity manager is either still loading the configuration or
2498 * failed to load. In either way, return from here as the dbus
2499 * match will take care */
2500 std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
2501 return;
2502 }
2503
2504 /* There should be only one HSBP Configureation exposed */
2505 if (subtree.size() != 1)
2506 {
2507 std::cerr << __FUNCTION__
2508 << ": Multiple configurations "
2509 "detected !!\n";
2510 /* Critical Error. Stop Application */
2511 stopHsbpManager();
2512 return;
2513 }
2514
2515 auto& path = subtree.begin()->first;
2516 auto& objDict = subtree.begin()->second;
2517
2518 if (objDict.empty())
2519 {
2520 /* Critical Error. Stop Application */
2521 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2522 stopHsbpManager();
2523 return;
2524 }
2525
2526 const std::string& service = objDict.begin()->first;
2527
2528 conn->async_method_call(
2529 [](const boost::system::error_code er,
2530 const boost::container::flat_map<std::string,
2531 BasicVariantType>& resp) {
2532 if (er)
2533 {
2534 std::cerr << __FUNCTION__ << ": Error Getting Config "
2535 << er.message() << "\n";
2536 /* Critical Error. Stop Application */
2537 stopHsbpManager();
2538 return;
2539 }
2540
2541 std::optional<uint64_t> rootI2cBus;
2542 std::optional<std::vector<std::string>> supportedHsbps;
2543 std::optional<std::vector<std::string>> clockBufferTypes;
2544 std::optional<std::vector<std::string>> ioExpanderTypes;
2545
2546 /* Loop through to get root i2c bus and list of supported
2547 * HSBPs */
2548 for (const auto& [key, value] : resp)
2549 {
2550 if (key == "HsbpSupported")
2551 {
2552 supportedHsbps =
2553 std::get<std::vector<std::string>>(value);
2554 }
2555 else if (key == "RootI2cBus")
2556 {
2557 rootI2cBus = std::get<uint64_t>(value);
2558 }
2559 else if (key == "ClockBuffer")
2560 {
2561 clockBufferTypes =
2562 std::get<std::vector<std::string>>(value);
2563 }
2564 else if (key == "IoExpander")
2565 {
2566 ioExpanderTypes =
2567 std::get<std::vector<std::string>>(value);
2568 }
2569 }
2570
2571 /* Verify if i2c bus, supported HSBP's and clock buffers
2572 * were defined (IO Expanders are optional) */
2573 if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
2574 {
2575 std::cerr << __FUNCTION__
2576 << ": Incomplete HSBP "
2577 "configuration !! \n";
2578 /* Critical Error. Stop Application */
2579 stopHsbpManager();
2580 return;
2581 }
2582
2583 /* Clear and Load all details to global hsbp configuration
2584 * variable */
2585 hsbpConfig.clearConfig();
2586 hsbpConfig.rootBus = *rootI2cBus;
2587 hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
2588
2589 for (auto& clkBuffType : *clockBufferTypes)
2590 {
2591 hsbpConfig.clockBufferTypes.emplace_back(
2592 "xyz.openbmc_project.Configuration." + clkBuffType);
2593 }
2594
2595 if (ioExpanderTypes)
2596 {
2597 for (auto& ioCntrType : *ioExpanderTypes)
2598 {
2599 hsbpConfig.ioExpanderTypes.emplace_back(
2600 "xyz.openbmc_project.Configuration." +
2601 ioCntrType);
2602 }
2603 }
2604
2605 /* Loop through to get HSBP-NVME map and Components map
2606 * details */
2607 uint8_t hsbpMapCount = 0;
2608 for (const auto& [key, value] : resp)
2609 {
2610 if (std::find(hsbpConfig.supportedHsbps.begin(),
2611 hsbpConfig.supportedHsbps.end(),
2612 key) != hsbpConfig.supportedHsbps.end())
2613 {
2614 std::optional<std::vector<std::string>> hsbpMap;
2615 hsbpMap = std::get<NvmeMapping>(value);
2616 if (!hsbpMap)
2617 {
2618 break;
2619 }
2620 hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
2621 hsbpMapCount++;
2622 }
2623 }
2624
2625 /* Check if we were able to get all the HSBP-NVMe maps */
2626 if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
2627 {
2628 std::cerr << __FUNCTION__
2629 << ": Incomplete HSBP Map "
2630 "details !! \n";
2631 /* Critical Error. Stop Application */
2632 stopHsbpManager();
2633 return;
2634 }
2635
2636 /* HSBP configuration is loaded */
2637 appState = AppState::hsbpConfigLoaded;
2638 std::cerr << "HSBP Config loaded !\n";
2639
2640 /* Get Clock buffers and IO expander details. Create shared
2641 * object of AsyncCallbackHandler with success and error
2642 * callback */
2643 auto componentsLoadedCallback = std::make_shared<
2644 AsyncCallbackHandler>(
2645 []() {
2646 /* Verify if all components were initialized without
2647 * errors */
2648 if (!verifyComponentsLoaded())
2649 {
2650 /* The application cannot proceed further as
2651 * components initialization failed. App needs
2652 * Restart */
2653 appState = AppState::idle;
2654 std::cerr
2655 << "One or more Componenets initialization "
2656 "failed !! Restart Required !\n";
2657 stopHsbpManager();
2658 }
2659
2660 appState = AppState::componentsLoaded;
2661 setUpBackplanesAndDrives();
2662 },
2663 []() {
2664 /* The application cannot proceed further as
2665 * components load failed. App needs Restart */
2666 appState = AppState::idle;
2667 std::cerr << "Loading Componenets failed !! "
2668 "Restart Required !\n";
2669 stopHsbpManager();
2670 });
2671
2672 loadClockBufferInfo(componentsLoadedCallback);
2673
2674 if (ioExpanderTypes)
2675 {
2676 loadIoExpanderInfo(componentsLoadedCallback);
2677 }
2678 },
2679 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2680 hsbpConfigIntf);
2681 },
2682 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2683 0, std::array<const char*, 1>{hsbpConfigIntf});
2684}
2685
2686void setupHsbpConfigMatch()
2687{
2688 static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
2689 *conn,
2690 "sender='xyz.openbmc_project.EntityManager', type='signal', "
2691 "member='PropertiesChanged', "
2692 "interface='org.freedesktop.DBus.Properties', "
2693 "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2694 std::string(hsbpConfigIntf) + "'",
2695 [](sdbusplus::message_t& msg) {
2696 std::string intfName;
2697 boost::container::flat_map<std::string, BasicVariantType> values;
2698 msg.read(intfName, values);
2699
2700 /* This match will be triggered for each of the property being set
2701 * under the hsbpConfig interface. "HsbpSupported" is one of the
2702 * important property which will enable us to read other properties.
2703 * So, when the match event occurs for "HsbpSupported" property
2704 * being set, we will call "loadHsbpConfig()" If the control has
2705 * come here, its either the first initialization or entity-manager
2706 * reload. So, we will reset the state to uninitialized
2707 */
2708 for (const auto& [key, value] : values)
2709 {
2710 if (key == "HsbpSupported")
2711 {
2712 /* Configuration change detected, change the state to stop
2713 * other processing */
2714 appState = AppState::idle;
2715
2716 /* We will call the function after a small delay to let all
2717 * the properties to be intialized */
2718 auto loadTimer =
2719 std::make_shared<boost::asio::steady_timer>(io);
2720 loadTimer->expires_after(std::chrono::seconds(1));
2721 loadTimer->async_wait(
2722 [loadTimer](const boost::system::error_code ec) {
2723 if (ec == boost::asio::error::operation_aborted)
2724 {
2725 return;
2726 }
2727 else if (ec)
2728 {
2729 std::cerr << __FUNCTION__ << ": Timer error"
2730 << ec.message() << "\n";
2731 if (hsbpConfig.supportedHsbps.empty())
2732 {
2733 /* Critical Error as none of the
2734 * configuration was loaded and timer
2735 * failed. Stop the application */
2736 stopHsbpManager();
2737 }
2738 return;
2739 }
2740 loadHsbpConfig();
2741 });
2742 }
2743 }
2744 });
2745}
2746/***************************** End of Section *******************************/
2747
2748/****************************************************************************/
2749/***************** GPIO Events related Function Definitions *****************/
2750/****************************************************************************/
2751static void nvmeLvc3AlertHandler()
2752{
2753 /* If the state is not backplanesLoaded, we ignore the GPIO event as we
2754 * cannot communicate to the backplanes yet */
2755 if (appState < AppState::backplanesLoaded)
2756 {
2757 std::cerr << __FUNCTION__
2758 << ": HSBP not initialized ! Dropping the interrupt ! \n";
2759 return;
2760 }
2761
2762 /* This GPIO event only indicates the addition or removal of drive to either
2763 * of CPU. The backplanes detected need to be scanned and detect which drive
2764 * has been added/removed and enable/diable clock accordingly */
2765 gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
2766
2767 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
2768 {
2769 /* Check for HSBP Drives status to determine if any new drive has been
2770 * added/removed and update clocks accordingly */
2771 checkHsbpDrivesStatus();
2772 }
2773
2774 nvmeLvc3AlertEvent.async_wait(
2775 boost::asio::posix::stream_descriptor::wait_read,
2776 [](const boost::system::error_code ec) {
2777 if (ec)
2778 {
2779 std::cerr << __FUNCTION__
2780 << ": nvmealert event error: " << ec.message()
2781 << "\n";
2782 }
2783 nvmeLvc3AlertHandler();
2784 });
Feist, Jamesc95cf672019-08-29 16:10:35 -07002785}
2786
Rohit Chandel52639be2021-04-14 15:10:41 +05302787static bool hsbpRequestAlertGpioEvents(
2788 const std::string& name, const std::function<void()>& handler,
2789 gpiod::line& gpioLine,
2790 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
2791{
2792 // Find the GPIO line
2793 gpioLine = gpiod::find_line(name);
2794 if (!gpioLine)
2795 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002796 std::cerr << __FUNCTION__ << ": Failed to find the " << name
2797 << " line\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302798 return false;
2799 }
2800
2801 try
2802 {
2803 gpioLine.request(
2804 {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
2805 }
2806 catch (std::exception&)
2807 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002808 std::cerr << __FUNCTION__ << ": Failed to request events for " << name
2809 << "\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302810 return false;
2811 }
2812
2813 int gpioLineFd = gpioLine.event_get_fd();
2814 if (gpioLineFd < 0)
2815 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002816 std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302817 return false;
2818 }
2819
2820 gpioEventDescriptor.assign(gpioLineFd);
2821
2822 gpioEventDescriptor.async_wait(
2823 boost::asio::posix::stream_descriptor::wait_read,
2824 [&name, handler](const boost::system::error_code ec) {
2825 if (ec)
2826 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002827 std::cerr << __FUNCTION__ << ": " << name
2828 << " fd handler error: " << ec.message() << "\n";
Rohit Chandel52639be2021-04-14 15:10:41 +05302829 return;
2830 }
2831 handler();
2832 });
2833 return true;
2834}
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002835/***************************** End of Section *******************************/
Rohit Chandel52639be2021-04-14 15:10:41 +05302836
Feist, Jamesc95cf672019-08-29 16:10:35 -07002837int main()
2838{
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002839 std::cerr << "******* Starting hsbp-manager *******\n";
Feist, Jamesc95cf672019-08-29 16:10:35 -07002840
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002841 /* Set the Dbus name */
James Feistdb2e0e72019-10-07 16:34:06 -07002842 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07002843
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002844 /* Add interface for storage inventory */
2845 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
2846 "xyz.openbmc_project.inventory.item.storage");
Feist, Jamesc95cf672019-08-29 16:10:35 -07002847
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002848 /* HSBP initializtion flow:
2849 * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
2850 * 2. Set up Dbus match for power - determine if host is up and running
2851 * or powered off
2852 * 3. Set up Dbus match for HSBP backplanes and Drives
2853 * 4. Load HSBP config exposed by entity manager
2854 * - Also setup a match to capture HSBP configuation in case
2855 * entity-manager restarts
2856 * 5. Load Clock buffer and IO expander (and other peripherals if any
2857 * related to HSBP functionality)
2858 * - Reload the info each time HSBP configuration is changed
2859 * 6. Populate all Backpanes (HSBP's)
2860 * 7. Load all NVMe drive's and associate with HSBP Backpane
2861 */
James Feist0b236ab2019-10-02 09:09:16 -07002862
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002863 /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
2864 if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
2865 nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
2866 nvmeLvc3AlertEvent))
Rohit Chandel52639be2021-04-14 15:10:41 +05302867 {
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002868 std::cerr << __FUNCTION__
2869 << ": error: Unable to monitor events on HSBP "
2870 "Alert line\n";
2871 return -1;
Rohit Chandel52639be2021-04-14 15:10:41 +05302872 }
2873
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002874 /* Setup Dbus-match for power */
James Feist9f6565d2019-10-09 13:15:13 -07002875 setupPowerMatch(conn);
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002876
2877 /* Setup Dbus-match for HSBP backplanes and Drives */
2878 setupBackplanesAndDrivesMatch();
2879
2880 /* Setup HSBP Config match and load config
2881 * In the event of entity-manager reboot, the match will help catch new
2882 * configuration.
2883 * In the event of hsbp-manager reboot, loadHsbpConfig will get all
2884 * config details and will take care of remaining config's to be
2885 * loaded
2886 */
2887 setupHsbpConfigMatch();
2888 loadHsbpConfig();
2889
Feist, Jamesc95cf672019-08-29 16:10:35 -07002890 io.run();
P Dheeraj Srujan Kumar76c12d92022-12-29 13:02:36 -08002891 std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
2892 return -1;
Feist, Jamesc95cf672019-08-29 16:10:35 -07002893}