blob: 9bb136754fc56e90c62ab953d66c52d034588317 [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
Rohit Chandel52639be2021-04-14 15:10:41 +053019#include <bitset>
Feist, Jamesc95cf672019-08-29 16:10:35 -070020#include <boost/algorithm/string/replace.hpp>
Rohit Chandel52639be2021-04-14 15:10:41 +053021#include <boost/asio/posix/stream_descriptor.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070022#include <boost/asio/steady_timer.hpp>
James Feist8675a912019-10-16 14:36:58 -070023#include <boost/container/flat_set.hpp>
James Feist0b236ab2019-10-02 09:09:16 -070024#include <filesystem>
James Feist8675a912019-10-16 14:36:58 -070025#include <fstream>
Rohit Chandel52639be2021-04-14 15:10:41 +053026#include <gpiod.hpp>
Feist, Jamesc95cf672019-08-29 16:10:35 -070027#include <iostream>
28#include <sdbusplus/asio/connection.hpp>
29#include <sdbusplus/asio/object_server.hpp>
30#include <sdbusplus/bus/match.hpp>
31#include <string>
James Feist45772222019-09-27 10:38:08 -070032#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070033
34extern "C" {
35#include <i2c/smbus.h>
36#include <linux/i2c-dev.h>
37}
38
39constexpr const char* configType =
40 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
James Feistdb2e0e72019-10-07 16:34:06 -070041constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
Feist, Jamesc95cf672019-08-29 16:10:35 -070042
James Feist45772222019-09-27 10:38:08 -070043constexpr size_t scanRateSeconds = 5;
44constexpr size_t maxDrives = 8; // only 1 byte alloted
45
Feist, Jamesc95cf672019-08-29 16:10:35 -070046boost::asio::io_context io;
47auto conn = std::make_shared<sdbusplus::asio::connection>(io);
48sdbusplus::asio::object_server objServer(conn);
49
Rohit Chandel52639be2021-04-14 15:10:41 +053050// GPIO Lines and Event Descriptors
51static gpiod::line nvmeLvc3AlertLine;
52static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
53
James Feist45772222019-09-27 10:38:08 -070054static std::string zeroPad(const uint8_t val)
55{
56 std::ostringstream version;
57 version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val);
58 return version.str();
59}
60
James Feist0b236ab2019-10-02 09:09:16 -070061struct Mux
62{
James Feist8675a912019-10-16 14:36:58 -070063 Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
64 bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
James Feist0b236ab2019-10-02 09:09:16 -070065 {
66 }
67 size_t bus;
68 size_t address;
James Feist8675a912019-10-16 14:36:58 -070069 size_t channels;
70 size_t index;
71
72 // to sort in the flat set
73 bool operator<(const Mux& rhs) const
74 {
75 return index < rhs.index;
76 }
James Feist0b236ab2019-10-02 09:09:16 -070077};
James Feist09dd2312019-10-09 09:29:03 -070078
79enum class BlinkPattern : uint8_t
80{
81 off = 0x0,
82 error = 0x2,
83 terminate = 0x3
84};
85
86struct Led : std::enable_shared_from_this<Led>
87{
88 // led pattern addresses start at 0x10
89 Led(const std::string& path, size_t index, int fd) :
90 address(static_cast<uint8_t>(index + 0x10)), file(fd),
91 ledInterface(objServer.add_interface(path, ledGroup::interface))
92 {
93 if (index >= maxDrives)
94 {
95 throw std::runtime_error("Invalid drive index");
96 }
97
98 if (!set(BlinkPattern::off))
99 {
100 std::cerr << "Cannot initialize LED " << path << "\n";
101 }
102 }
103
104 // this has to be called outside the constructor for shared_from_this to
105 // work
106 void createInterface(void)
107 {
108 std::shared_ptr<Led> self = shared_from_this();
109
110 ledInterface->register_property(
111 ledGroup::asserted, false, [self](const bool req, bool& val) {
112 if (req == val)
113 {
114 return 1;
115 }
James Feist9f6565d2019-10-09 13:15:13 -0700116
117 if (!isPowerOn())
118 {
119 std::cerr << "Can't change blink state when power is off\n";
120 throw std::runtime_error(
121 "Can't change blink state when power is off");
122 }
James Feist09dd2312019-10-09 09:29:03 -0700123 BlinkPattern pattern =
124 req ? BlinkPattern::error : BlinkPattern::terminate;
125 if (!self->set(pattern))
126 {
James Feist9f6565d2019-10-09 13:15:13 -0700127 std::cerr << "Can't change blink pattern\n";
James Feist09dd2312019-10-09 09:29:03 -0700128 throw std::runtime_error("Cannot set blink pattern");
129 }
130 val = req;
131 return 1;
132 });
133 ledInterface->initialize();
134 }
135
136 virtual ~Led()
137 {
138 objServer.remove_interface(ledInterface);
139 }
140
141 bool set(BlinkPattern pattern)
142 {
143 int ret = i2c_smbus_write_byte_data(file, address,
144 static_cast<uint8_t>(pattern));
145 return ret >= 0;
146 }
147
148 uint8_t address;
149 int file;
150 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
151};
152
James Feist45772222019-09-27 10:38:08 -0700153struct Drive
154{
James Feist42b49c12019-10-29 15:18:43 -0700155 Drive(size_t driveIndex, bool present, bool isOperational, bool nvme,
James Feist244f3232019-09-27 15:15:14 -0700156 bool rebuilding) :
James Feist42b49c12019-10-29 15:18:43 -0700157 isNvme(nvme),
James Feiste8818522019-11-04 13:36:10 -0800158 isPresent(present), index(driveIndex)
James Feist45772222019-09-27 10:38:08 -0700159 {
160 constexpr const char* basePath =
161 "/xyz/openbmc_project/inventory/item/drive/Drive_";
162 itemIface = objServer.add_interface(
163 basePath + std::to_string(driveIndex), inventory::interface);
164 itemIface->register_property("Present", isPresent);
165 itemIface->register_property("PrettyName",
166 "Drive " + std::to_string(driveIndex));
167 itemIface->initialize();
168 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700169 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700170 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feist42b49c12019-10-29 15:18:43 -0700171
172 operationalIface->register_property(
173 "Functional", isOperational,
174 [this](const bool req, bool& property) {
175 if (!isPresent)
176 {
177 return 0;
178 }
179 if (property == req)
180 {
181 return 1;
182 }
183 property = req;
184 if (req)
185 {
186 clearFailed();
187 return 1;
188 }
189 markFailed();
190 return 1;
191 });
192
James Feist45772222019-09-27 10:38:08 -0700193 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700194 rebuildingIface = objServer.add_interface(
195 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
196 rebuildingIface->register_property("Rebuilding", rebuilding);
197 rebuildingIface->initialize();
James Feist8675a912019-10-16 14:36:58 -0700198 driveIface =
199 objServer.add_interface(itemIface->get_object_path(),
200 "xyz.openbmc_project.Inventory.Item.Drive");
201 driveIface->initialize();
James Feist42b49c12019-10-29 15:18:43 -0700202 associations = objServer.add_interface(itemIface->get_object_path(),
203 association::interface);
204 associations->register_property("Associations",
205 std::vector<Association>{});
206 associations->initialize();
207
208 if (isPresent && (!isOperational || rebuilding))
209 {
210 markFailed();
211 }
James Feist45772222019-09-27 10:38:08 -0700212 }
James Feist09dd2312019-10-09 09:29:03 -0700213 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700214 {
215 objServer.remove_interface(itemIface);
216 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700217 objServer.remove_interface(rebuildingIface);
James Feist8675a912019-10-16 14:36:58 -0700218 objServer.remove_interface(assetIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700219 objServer.remove_interface(driveIface);
James Feist42b49c12019-10-29 15:18:43 -0700220 objServer.remove_interface(associations);
James Feist0b236ab2019-10-02 09:09:16 -0700221 }
222
James Feistc66735b2020-07-17 13:51:21 -0700223 void removeAsset()
224 {
225 objServer.remove_interface(assetIface);
226 assetIface = nullptr;
227 }
228
James Feist8675a912019-10-16 14:36:58 -0700229 void createAsset(
230 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700231 {
James Feist8675a912019-10-16 14:36:58 -0700232 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700233 {
234 return;
235 }
James Feist8675a912019-10-16 14:36:58 -0700236 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700237 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700238 "xyz.openbmc_project.Inventory.Decorator.Asset");
239 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700240 {
James Feist8675a912019-10-16 14:36:58 -0700241 assetIface->register_property(key, value);
James Feistd86629c2020-04-23 10:07:25 -0700242 if (key == "SerialNumber")
243 {
244 serialNumber = value;
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700245 serialNumberInitialized = true;
James Feistd86629c2020-04-23 10:07:25 -0700246 }
James Feistdb2e0e72019-10-07 16:34:06 -0700247 }
James Feist8675a912019-10-16 14:36:58 -0700248 assetIface->initialize();
James Feistdb2e0e72019-10-07 16:34:06 -0700249 }
250
James Feist42b49c12019-10-29 15:18:43 -0700251 void markFailed(void)
252 {
253 // todo: maybe look this up via mapper
254 constexpr const char* globalInventoryPath =
255 "/xyz/openbmc_project/CallbackManager";
256
257 if (!isPresent)
258 {
259 return;
260 }
261
262 operationalIface->set_property("Functional", false);
263 std::vector<Association> warning = {
264 {"", "warning", globalInventoryPath}};
265 associations->set_property("Associations", warning);
James Feiste8818522019-11-04 13:36:10 -0800266 logDriveError("Drive " + std::to_string(index));
James Feist42b49c12019-10-29 15:18:43 -0700267 }
268
269 void clearFailed(void)
270 {
271 operationalIface->set_property("Functional", true);
272 associations->set_property("Associations", std::vector<Association>{});
273 }
274
James Feistd86629c2020-04-23 10:07:25 -0700275 void setPresent(bool set)
James Feiste8818522019-11-04 13:36:10 -0800276 {
277 // nvme drives get detected by their fru
James Feistd86629c2020-04-23 10:07:25 -0700278 if (set == isPresent)
James Feiste8818522019-11-04 13:36:10 -0800279 {
280 return;
281 }
282 itemIface->set_property("Present", set);
283 isPresent = set;
James Feistd86629c2020-04-23 10:07:25 -0700284 }
285
286 void logPresent()
287 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700288 if (isNvme && !serialNumberInitialized)
James Feiste8818522019-11-04 13:36:10 -0800289 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700290 // wait until NVMe asset is updated to include the serial number
291 // from the NVMe drive
James Feistd86629c2020-04-23 10:07:25 -0700292 return;
James Feiste8818522019-11-04 13:36:10 -0800293 }
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700294
295 if (!isPresent && loggedPresent)
296 {
297 loggedPresent = false;
298 logDeviceRemoved("Drive", std::to_string(index), serialNumber);
299 serialNumber = "N/A";
300 serialNumberInitialized = false;
James Feistc66735b2020-07-17 13:51:21 -0700301 removeAsset();
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700302 }
303 else if (isPresent && !loggedPresent)
304 {
305 loggedPresent = true;
306 logDeviceAdded("Drive", std::to_string(index), serialNumber);
307 }
James Feiste8818522019-11-04 13:36:10 -0800308 }
309
James Feist45772222019-09-27 10:38:08 -0700310 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
311 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700312 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700313 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700314 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700315 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700316
James Feist45772222019-09-27 10:38:08 -0700317 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700318 bool isPresent;
James Feiste8818522019-11-04 13:36:10 -0800319 size_t index;
James Feistd86629c2020-04-23 10:07:25 -0700320 std::string serialNumber = "N/A";
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700321 bool serialNumberInitialized = false;
James Feistd86629c2020-04-23 10:07:25 -0700322 bool loggedPresent = false;
James Feist45772222019-09-27 10:38:08 -0700323};
324
James Feistd86629c2020-04-23 10:07:25 -0700325struct Backplane : std::enable_shared_from_this<Backplane>
Feist, Jamesc95cf672019-08-29 16:10:35 -0700326{
327
James Feist45772222019-09-27 10:38:08 -0700328 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
329 const std::string& nameIn) :
330 bus(busIn),
331 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feistd86629c2020-04-23 10:07:25 -0700332 timer(boost::asio::steady_timer(io)),
James Feist8675a912019-10-16 14:36:58 -0700333 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700334 {
335 }
James Feistd0d36f12019-11-21 10:19:44 -0800336 void populateAsset(const std::string& rootPath, const std::string& busname)
337 {
338 conn->async_method_call(
339 [assetIface{assetInterface}, hsbpIface{hsbpItemIface}](
340 const boost::system::error_code ec,
341 const boost::container::flat_map<
342 std::string, std::variant<std::string>>& values) mutable {
343 if (ec)
344 {
345 std::cerr
346 << "Error getting asset tag from HSBP configuration\n";
347
348 return;
349 }
350 assetIface = objServer.add_interface(
351 hsbpIface->get_object_path(), assetTag);
352 for (const auto& [key, value] : values)
353 {
354 const std::string* ptr = std::get_if<std::string>(&value);
355 if (ptr == nullptr)
356 {
357 std::cerr << key << " Invalid type!\n";
358 continue;
359 }
360 assetIface->register_property(key, *ptr);
361 }
362 assetIface->initialize();
363 },
364 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
365 assetTag);
366 }
367
368 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700369 {
James Feist09dd2312019-10-09 09:29:03 -0700370 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
371 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700372 if (file < 0)
373 {
374 std::cerr << "unable to open bus " << bus << "\n";
375 return;
376 }
377
378 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
379 {
380 std::cerr << "unable to set address to " << address << "\n";
381 return;
382 }
383
James Feist45772222019-09-27 10:38:08 -0700384 if (!getPresent())
385 {
386 std::cerr << "Cannot detect CPLD\n";
387 return;
388 }
389
390 getBootVer(bootVer);
391 getFPGAVer(fpgaVer);
392 getSecurityRev(securityRev);
393 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700394 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700395 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700396 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700397 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700398 hsbpItemIface->register_property("PrettyName", name);
399 hsbpItemIface->initialize();
400
James Feistd0d36f12019-11-21 10:19:44 -0800401 storageInterface = objServer.add_interface(
402 hsbpItemIface->get_object_path(),
403 "xyz.openbmc_project.Inventory.Item.StorageController");
404 storageInterface->initialize();
405
James Feist45772222019-09-27 10:38:08 -0700406 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800407 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700408 "xyz.openbmc_project.Software.Version");
409 versionIface->register_property("Version", zeroPad(bootVer) + "." +
410 zeroPad(fpgaVer) + "." +
411 zeroPad(securityRev));
412 versionIface->register_property(
413 "Purpose",
414 std::string(
415 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
416 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000417
418 auto activationIface =
419 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
420 "xyz.openbmc_project.Software.Activation");
421
422 activationIface->register_property(
423 "Activation",
424 std::string(
425 "xyz.openbmc_project.Software.Activation.Activations.Active"));
426 activationIface->register_property(
427 "RequestedActivation",
428 std::string("xyz.openbmc_project.Software.Activation."
429 "RequestedActivations.None"));
430
431 activationIface->initialize();
432
James Feist45772222019-09-27 10:38:08 -0700433 getPresence(presence);
434 getIFDET(ifdet);
435
James Feistd0d36f12019-11-21 10:19:44 -0800436 populateAsset(rootPath, busname);
437
James Feist45772222019-09-27 10:38:08 -0700438 createDrives();
439
440 runTimer();
441 }
442
443 void runTimer()
444 {
James Feistd86629c2020-04-23 10:07:25 -0700445 timer.expires_after(std::chrono::seconds(scanRateSeconds));
446 timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
447 boost::system::error_code ec) {
448 auto self = weak.lock();
449 if (!self)
450 {
451 return;
452 }
James Feist45772222019-09-27 10:38:08 -0700453 if (ec == boost::asio::error::operation_aborted)
454 {
455 // we're being destroyed
456 return;
457 }
458 else if (ec)
459 {
460 std::cerr << "timer error " << ec.message() << "\n";
461 return;
462 }
James Feist9f6565d2019-10-09 13:15:13 -0700463
464 if (!isPowerOn())
465 {
466 // can't access hsbp when power is off
James Feistd86629c2020-04-23 10:07:25 -0700467 self->runTimer();
James Feist9f6565d2019-10-09 13:15:13 -0700468 return;
469 }
James Feist45772222019-09-27 10:38:08 -0700470
James Feistd86629c2020-04-23 10:07:25 -0700471 self->getPresence(self->presence);
472 self->getIFDET(self->ifdet);
473 self->getFailed(self->failed);
474 self->getRebuild(self->rebuilding);
James Feist45772222019-09-27 10:38:08 -0700475
James Feistd86629c2020-04-23 10:07:25 -0700476 self->updateDrives();
477 self->runTimer();
James Feist45772222019-09-27 10:38:08 -0700478 });
479 }
480
481 void createDrives()
482 {
James Feist45772222019-09-27 10:38:08 -0700483 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700484 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700485 uint8_t driveSlot = (1 << ii);
486 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
487 bool isPresent = isNvme || (presence & driveSlot);
488 bool isFailed = !isPresent || failed & driveSlot;
489 bool isRebuilding = !isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -0700490
491 // +1 to convert from 0 based to 1 based
492 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700493 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
494 isNvme, isRebuilding);
495 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
496 drive.itemIface->get_object_path(), ii, file));
497 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700498 }
499 }
500
James Feist45772222019-09-27 10:38:08 -0700501 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700502 {
James Feist42b49c12019-10-29 15:18:43 -0700503 size_t ii = 0;
504
505 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700506 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700507 uint8_t driveSlot = (1 << ii);
508 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
509 bool isPresent = isNvme || (presence & driveSlot);
510 bool isFailed = !isPresent || (failed & driveSlot);
511 bool isRebuilding = isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -0700512
James Feist42b49c12019-10-29 15:18:43 -0700513 it->isNvme = isNvme;
James Feistd86629c2020-04-23 10:07:25 -0700514 it->setPresent(isPresent);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700515 it->logPresent();
James Feistda0c35f2020-02-03 10:16:13 -0800516
James Feist42b49c12019-10-29 15:18:43 -0700517 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
518 if (isFailed || isRebuilding)
519 {
520 it->markFailed();
521 }
522 else
523 {
524 it->clearFailed();
525 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700526 }
James Feist45772222019-09-27 10:38:08 -0700527 }
528
529 bool getPresent()
530 {
531 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700532 return present;
533 }
James Feist45772222019-09-27 10:38:08 -0700534
535 bool getTypeID(uint8_t& val)
536 {
537 constexpr uint8_t addr = 2;
538 int ret = i2c_smbus_read_byte_data(file, addr);
539 if (ret < 0)
540 {
541 std::cerr << "Error " << __FUNCTION__ << "\n";
542 return false;
543 }
544 val = static_cast<uint8_t>(ret);
545 return true;
546 }
547
548 bool getBootVer(uint8_t& val)
549 {
550 constexpr uint8_t addr = 3;
551 int ret = i2c_smbus_read_byte_data(file, addr);
552 if (ret < 0)
553 {
554 std::cerr << "Error " << __FUNCTION__ << "\n";
555 return false;
556 }
557 val = static_cast<uint8_t>(ret);
558 return true;
559 }
560
561 bool getFPGAVer(uint8_t& val)
562 {
563 constexpr uint8_t addr = 4;
564 int ret = i2c_smbus_read_byte_data(file, addr);
565 if (ret < 0)
566 {
567 std::cerr << "Error " << __FUNCTION__ << "\n";
568 return false;
569 }
570 val = static_cast<uint8_t>(ret);
571 return true;
572 }
573
574 bool getSecurityRev(uint8_t& val)
575 {
576 constexpr uint8_t addr = 5;
577 int ret = i2c_smbus_read_byte_data(file, addr);
578 if (ret < 0)
579 {
580 std::cerr << "Error " << __FUNCTION__ << "\n";
581 return false;
582 }
583 val = static_cast<uint8_t>(ret);
584 return true;
585 }
586
587 bool getPresence(uint8_t& val)
588 {
589 // NVMe drives do not assert PRSNTn, and as such do not get reported as
590 // PRESENT in this register
591
592 constexpr uint8_t addr = 8;
593
594 int ret = i2c_smbus_read_byte_data(file, addr);
595 if (ret < 0)
596 {
597 std::cerr << "Error " << __FUNCTION__ << "\n";
598 return false;
599 }
600 // presence is inverted
601 val = static_cast<uint8_t>(~ret);
602 return true;
603 }
604
605 bool getIFDET(uint8_t& val)
606 {
607 // This register is a bitmap of parallel GPIO pins connected to the
608 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
609 // IFDETn low when they are inserted into the HSBP.This register, in
610 // combination with the PRESENCE register, are used by the BMC to detect
611 // the presence of NVMe drives.
612
613 constexpr uint8_t addr = 9;
614
615 int ret = i2c_smbus_read_byte_data(file, addr);
616 if (ret < 0)
617 {
618 std::cerr << "Error " << __FUNCTION__ << "\n";
619 return false;
620 }
621 // ifdet is inverted
622 val = static_cast<uint8_t>(~ret);
623 return true;
624 }
625
626 bool getFailed(uint8_t& val)
627 {
628 constexpr uint8_t addr = 0xC;
629 int ret = i2c_smbus_read_byte_data(file, addr);
630 if (ret < 0)
631 {
632 std::cerr << "Error " << __FUNCTION__ << "\n";
633 return false;
634 }
635 val = static_cast<uint8_t>(ret);
636 return true;
637 }
638
639 bool getRebuild(uint8_t& val)
640 {
641 constexpr uint8_t addr = 0xD;
642 int ret = i2c_smbus_read_byte_data(file, addr);
643 if (ret < 0)
644 {
James Feistd86629c2020-04-23 10:07:25 -0700645 std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
646 << "\n";
James Feist45772222019-09-27 10:38:08 -0700647 return false;
648 }
649 val = static_cast<uint8_t>(ret);
650 return true;
651 }
652
James Feist09dd2312019-10-09 09:29:03 -0700653 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700654 {
655 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700656 objServer.remove_interface(versionIface);
James Feistd86629c2020-04-23 10:07:25 -0700657 timer.cancel();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700658 if (file >= 0)
659 {
660 close(file);
661 }
662 }
663
664 size_t bus;
665 size_t address;
James Feist45772222019-09-27 10:38:08 -0700666 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700667 std::string name;
James Feistd86629c2020-04-23 10:07:25 -0700668 boost::asio::steady_timer timer;
James Feist45772222019-09-27 10:38:08 -0700669 bool present = false;
670 uint8_t typeId = 0;
671 uint8_t bootVer = 0;
672 uint8_t fpgaVer = 0;
673 uint8_t securityRev = 0;
674 uint8_t funSupported = 0;
675 uint8_t presence = 0;
676 uint8_t ifdet = 0;
677 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700678 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700679
680 int file = -1;
681
Feist, Jamesc95cf672019-08-29 16:10:35 -0700682 std::string type;
683
684 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700685 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -0800686 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
687 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
James Feist45772222019-09-27 10:38:08 -0700688
James Feist42b49c12019-10-29 15:18:43 -0700689 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700690 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -0700691 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700692};
693
James Feistd86629c2020-04-23 10:07:25 -0700694std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
James Feist42b49c12019-10-29 15:18:43 -0700695std::list<Drive> ownerlessDrives; // drives without a backplane
Feist, Jamesc95cf672019-08-29 16:10:35 -0700696
James Feist8675a912019-10-16 14:36:58 -0700697static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -0700698{
James Feist8675a912019-10-16 14:36:58 -0700699 size_t count = 0;
700 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -0700701 {
James Feistd86629c2020-04-23 10:07:25 -0700702 count += backplane->drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700703 }
James Feist8675a912019-10-16 14:36:58 -0700704 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700705}
706
James Feist8675a912019-10-16 14:36:58 -0700707void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -0700708{
James Feist8675a912019-10-16 14:36:58 -0700709 static constexpr const char* nvmeType =
710 "xyz.openbmc_project.Inventory.Item.NVMe";
James Feist0b236ab2019-10-02 09:09:16 -0700711
712 conn->async_method_call(
713 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
714 if (ec)
715 {
716 std::cerr << "Error contacting mapper " << ec.message() << "\n";
717 return;
718 }
James Feist8675a912019-10-16 14:36:58 -0700719
720 // drives may get an owner during this, or we might disover more
721 // drives
722 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -0700723 for (const auto& [path, objDict] : subtree)
724 {
725 if (objDict.empty())
726 {
727 continue;
728 }
729
730 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700731 // we export this interface too
732 if (owner == busName)
733 {
734 continue;
735 }
James Feist8675a912019-10-16 14:36:58 -0700736 if (std::find(objDict.begin()->second.begin(),
737 objDict.begin()->second.end(),
738 assetTag) == objDict.begin()->second.end())
739 {
740 // no asset tag to associate to
741 continue;
742 }
743
James Feist0b236ab2019-10-02 09:09:16 -0700744 conn->async_method_call(
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700745 [path](const boost::system::error_code ec2,
746 const boost::container::flat_map<
747 std::string,
748 std::variant<uint64_t, std::string>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700749 if (ec2)
750 {
751 std::cerr << "Error Getting Config "
752 << ec2.message() << " " << __FUNCTION__
753 << "\n";
754 return;
755 }
756 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -0700757
James Feist8675a912019-10-16 14:36:58 -0700758 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -0700759 {
760 std::cerr << "Illegal interface at " << path
761 << "\n";
762 return;
763 }
764
James Feist8675a912019-10-16 14:36:58 -0700765 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -0700766 size_t muxBus = static_cast<size_t>(
767 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -0700768 std::filesystem::path muxPath =
769 "/sys/bus/i2c/devices/i2c-" +
770 std::to_string(muxBus) + "/mux_device";
771 if (!std::filesystem::is_symlink(muxPath))
772 {
773 std::cerr << path << " mux does not exist\n";
774 return;
775 }
776
James Feist8675a912019-10-16 14:36:58 -0700777 // we should be getting something of the form 7-0052
778 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -0700779 std::string fname =
780 std::filesystem::read_symlink(muxPath).filename();
781 auto findDash = fname.find('-');
782
783 if (findDash == std::string::npos ||
784 findDash + 1 >= fname.size())
785 {
786 std::cerr << path << " mux path invalid\n";
787 return;
788 }
789
790 std::string busStr = fname.substr(0, findDash);
791 std::string muxStr = fname.substr(findDash + 1);
792
793 size_t bus = static_cast<size_t>(std::stoi(busStr));
794 size_t addr =
795 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -0700796 size_t muxIndex = 0;
797
798 // find the channel of the mux the drive is on
799 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
800 std::to_string(muxBus) +
801 "/name");
802 if (!nameFile)
803 {
804 std::cerr << "Unable to open name file of bus "
805 << muxBus << "\n";
806 return;
807 }
808
809 std::string nameStr;
810 std::getline(nameFile, nameStr);
811
812 // file is of the form "i2c-4-mux (chan_id 1)", get chan
813 // assume single digit chan
814 const std::string prefix = "chan_id ";
815 size_t findId = nameStr.find(prefix);
816 if (findId == std::string::npos ||
817 findId + 1 >= nameStr.size())
818 {
819 std::cerr << "Illegal name file on bus " << muxBus
820 << "\n";
821 }
822
823 std::string indexStr =
824 nameStr.substr(findId + prefix.size(), 1);
825
826 size_t driveIndex = std::stoi(indexStr);
827
James Feist0b236ab2019-10-02 09:09:16 -0700828 Backplane* parent = nullptr;
829 for (auto& [name, backplane] : backplanes)
830 {
James Feist8675a912019-10-16 14:36:58 -0700831 muxIndex = 0;
James Feistd86629c2020-04-23 10:07:25 -0700832 for (const Mux& mux : *(backplane->muxes))
James Feist0b236ab2019-10-02 09:09:16 -0700833 {
834 if (bus == mux.bus && addr == mux.address)
835 {
James Feistd86629c2020-04-23 10:07:25 -0700836 parent = backplane.get();
James Feist0b236ab2019-10-02 09:09:16 -0700837 break;
838 }
James Feist8675a912019-10-16 14:36:58 -0700839 muxIndex += mux.channels;
James Feist0b236ab2019-10-02 09:09:16 -0700840 }
841 }
James Feist8675a912019-10-16 14:36:58 -0700842 boost::container::flat_map<std::string, std::string>
843 assetInventory;
844 const std::array<const char*, 4> assetKeys = {
845 "PartNumber", "SerialNumber", "Manufacturer",
846 "Model"};
847 for (const auto& [key, value] : values)
848 {
849 if (std::find(assetKeys.begin(), assetKeys.end(),
850 key) == assetKeys.end())
851 {
852 continue;
853 }
854 assetInventory[key] = std::get<std::string>(value);
855 }
856
857 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -0700858 if (parent == nullptr)
859 {
James Feist8675a912019-10-16 14:36:58 -0700860 auto& drive = ownerlessDrives.emplace_back(
861 getDriveCount() + 1, true, true, true, false);
862 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700863 return;
864 }
James Feist8675a912019-10-16 14:36:58 -0700865
866 driveIndex += muxIndex;
867
James Feist0b236ab2019-10-02 09:09:16 -0700868 if (parent->drives.size() <= driveIndex)
869 {
James Feist0b236ab2019-10-02 09:09:16 -0700870 std::cerr << "Illegal drive index at " << path
871 << " " << driveIndex << "\n";
872 return;
873 }
James Feist42b49c12019-10-29 15:18:43 -0700874 auto it = parent->drives.begin();
875 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -0700876
James Feist42b49c12019-10-29 15:18:43 -0700877 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700878 },
879 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -0700880 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -0700881 }
882 },
883 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
James Feist8675a912019-10-16 14:36:58 -0700884 0, std::array<const char*, 1>{nvmeType});
James Feist0b236ab2019-10-02 09:09:16 -0700885}
886
James Feist8675a912019-10-16 14:36:58 -0700887void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -0700888 std::string& rootPath)
889{
890 const static std::array<const std::string, 4> muxTypes = {
891 "xyz.openbmc_project.Configuration.PCA9543Mux",
892 "xyz.openbmc_project.Configuration.PCA9544Mux",
893 "xyz.openbmc_project.Configuration.PCA9545Mux",
894 "xyz.openbmc_project.Configuration.PCA9546Mux"};
895 conn->async_method_call(
896 [muxes](const boost::system::error_code ec,
897 const GetSubTreeType& subtree) {
898 if (ec)
899 {
900 std::cerr << "Error contacting mapper " << ec.message() << "\n";
901 return;
902 }
903 std::shared_ptr<std::function<void()>> callback =
904 std::make_shared<std::function<void()>>(
James Feist8675a912019-10-16 14:36:58 -0700905 []() { updateAssets(); });
906 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -0700907 for (const auto& [path, objDict] : subtree)
908 {
909 if (objDict.empty() || objDict.begin()->second.empty())
910 {
911 continue;
912 }
913
914 const std::string& owner = objDict.begin()->first;
915 const std::vector<std::string>& interfaces =
916 objDict.begin()->second;
917
918 const std::string* interface = nullptr;
919 for (const std::string& iface : interfaces)
920 {
921 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
922 muxTypes.end())
923 {
924 interface = &iface;
925 break;
926 }
927 }
928 if (interface == nullptr)
929 {
930 std::cerr << "Cannot get mux type\n";
931 continue;
932 }
933
934 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700935 [path, muxes, callback, index](
James Feist0b236ab2019-10-02 09:09:16 -0700936 const boost::system::error_code ec2,
937 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -0700938 std::string,
939 std::variant<uint64_t, std::vector<std::string>>>&
940 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700941 if (ec2)
942 {
943 std::cerr << "Error Getting Config "
944 << ec2.message() << " " << __FUNCTION__
945 << "\n";
946 return;
947 }
948 auto findBus = values.find("Bus");
949 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -0700950 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -0700951 if (findBus == values.end() ||
952 findAddress == values.end())
953 {
954 std::cerr << "Illegal configuration at " << path
955 << "\n";
956 return;
957 }
958 size_t bus = static_cast<size_t>(
959 std::get<uint64_t>(findBus->second));
960 size_t address = static_cast<size_t>(
961 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -0700962 std::vector<std::string> channels =
963 std::get<std::vector<std::string>>(
964 findChannelNames->second);
965 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -0700966 if (callback.use_count() == 1)
967 {
968 (*callback)();
969 }
970 },
971 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
972 *interface);
James Feist8675a912019-10-16 14:36:58 -0700973 index++;
James Feist0b236ab2019-10-02 09:09:16 -0700974 }
975 },
976 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
977 rootPath, 1, muxTypes);
978}
979
Feist, Jamesc95cf672019-08-29 16:10:35 -0700980void populate()
981{
Jayaprakash Mutyala0c5059f2021-11-10 22:09:55 +0000982 backplanes.clear();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700983 conn->async_method_call(
984 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
985 if (ec)
986 {
987 std::cerr << "Error contacting mapper " << ec.message() << "\n";
988 return;
989 }
990 for (const auto& [path, objDict] : subtree)
991 {
992 if (objDict.empty())
993 {
994 continue;
995 }
996
997 const std::string& owner = objDict.begin()->first;
998 conn->async_method_call(
James Feistd0d36f12019-11-21 10:19:44 -0800999 [path, owner](const boost::system::error_code ec2,
1000 const boost::container::flat_map<
1001 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001002 if (ec2)
1003 {
1004 std::cerr << "Error Getting Config "
1005 << ec2.message() << "\n";
1006 return;
1007 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001008 std::optional<size_t> bus;
1009 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -07001010 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001011 std::optional<std::string> name;
1012 for (const auto& [key, value] : resp)
1013 {
1014 if (key == "Bus")
1015 {
1016 bus = std::get<uint64_t>(value);
1017 }
1018 else if (key == "Address")
1019 {
1020 address = std::get<uint64_t>(value);
1021 }
James Feist45772222019-09-27 10:38:08 -07001022 else if (key == "Index")
1023 {
1024 backplaneIndex = std::get<uint64_t>(value);
1025 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001026 else if (key == "Name")
1027 {
1028 name = std::get<std::string>(value);
1029 }
1030 }
James Feist45772222019-09-27 10:38:08 -07001031 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001032 {
1033 std::cerr << "Illegal configuration at " << path
1034 << "\n";
1035 return;
1036 }
James Feist0b236ab2019-10-02 09:09:16 -07001037 std::string parentPath =
1038 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001039 const auto& [backplane, status] = backplanes.emplace(
James Feistd86629c2020-04-23 10:07:25 -07001040 *name, std::make_shared<Backplane>(
1041 *bus, *address, *backplaneIndex, *name));
1042 backplane->second->run(parentPath, owner);
1043 populateMuxes(backplane->second->muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001044 },
1045 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1046 configType);
1047 }
1048 },
1049 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1050 0, std::array<const char*, 1>{configType});
1051}
1052
Rohit Chandel52639be2021-04-14 15:10:41 +05301053static bool hsbpRequestAlertGpioEvents(
1054 const std::string& name, const std::function<void()>& handler,
1055 gpiod::line& gpioLine,
1056 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1057{
1058 // Find the GPIO line
1059 gpioLine = gpiod::find_line(name);
1060 if (!gpioLine)
1061 {
1062 std::cerr << "Failed to find the " << name << " line\n";
1063 return false;
1064 }
1065
1066 try
1067 {
1068 gpioLine.request(
1069 {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
1070 }
1071 catch (std::exception&)
1072 {
1073 std::cerr << "Failed to request events for " << name << "\n";
1074 return false;
1075 }
1076
1077 int gpioLineFd = gpioLine.event_get_fd();
1078 if (gpioLineFd < 0)
1079 {
1080 std::cerr << "Failed to get " << name << " fd\n";
1081 return false;
1082 }
1083
1084 gpioEventDescriptor.assign(gpioLineFd);
1085
1086 gpioEventDescriptor.async_wait(
1087 boost::asio::posix::stream_descriptor::wait_read,
1088 [&name, handler](const boost::system::error_code ec) {
1089 if (ec)
1090 {
1091 std::cerr << name << " fd handler error: " << ec.message()
1092 << "\n";
1093 return;
1094 }
1095 handler();
1096 });
1097 return true;
1098}
1099
1100/******************************************************************************************
1101 * HSBP Position CPLD SMB Address
1102 * 1 0xD0(0x68 7 bit)
1103 * 2 0xD2(0x69 7 bit)
1104 * we have max 2 HSBP per system. Closed chassis systems will either have 0 or
1105 * 2 HSBP's.
1106 *******************************************************************************************/
1107static constexpr uint8_t hsbpI2cBus = 4;
1108static constexpr uint8_t allDrivesWithStatusBit = 17;
1109static constexpr uint8_t statusAllDrives = (allDrivesWithStatusBit - 1);
1110static constexpr uint8_t allClockBitsDb2000 = 25;
1111static constexpr uint8_t statusAllClocksDb2000 = (allClockBitsDb2000 - 1);
1112static constexpr uint8_t singleDriveWithStatusBit = 9;
1113static constexpr uint8_t statusSingleDrive = (singleDriveWithStatusBit - 1);
1114static constexpr uint8_t maxDrivesPerHsbp = 8;
1115
1116static std::bitset<allDrivesWithStatusBit> drivePresenceStatus;
1117static std::bitset<allClockBitsDb2000> driveClockStatus;
1118static constexpr uint8_t hsbpCpldSmbaddr1 = 0x68;
1119static constexpr uint8_t hsbpCpldSmbaddr2 = 0x69;
1120static constexpr uint8_t hsbpCpldReg8 = 0x8;
1121static constexpr uint8_t hsbpCpldReg9 = 0x9;
1122static constexpr uint8_t db2000SlaveAddr = 0x6d;
1123static constexpr uint8_t db2000RegByte0 = 0x80;
1124static constexpr uint8_t db2000RegByte1 = 0x81;
1125static constexpr uint8_t db2000RegByte2 = 0x82;
1126static int hsbpFd;
1127
1128/********************************************************************
1129 * DB2000 Programming guide for PCIe Clocks enable/disable
1130 * CPU 0
1131 * =================================================================
1132 * slot Byte bit number
1133 * Position position
1134 * =================================================================
1135 * 7 0 5
1136 * 6 0 4
1137 * 5 0 3
1138 * 4 2 7
1139 * 3 1 3
1140 * 2 1 2
1141 * 1 1 1
1142 * 0 1 0
1143 *
1144 * CPU 1
1145 * =================================================================
1146 * slot Byte bit number
1147 * Position position
1148 * =================================================================
1149 * 7 1 6
1150 * 6 1 7
1151 * 5 2 0
1152 * 4 2 1
1153 * 3 1 5
1154 * 2 2 4
1155 * 1 2 2
1156 * 0 2 3
1157 *********************************************************************/
1158std::optional<int>
1159 updateClocksStatus(std::bitset<allDrivesWithStatusBit> nvmeDriveStatus)
1160{
1161 std::bitset<allClockBitsDb2000> nvmeClockStatus;
1162 /* mapping table for nvme drive index(0-15) to DB2000 register bit fields */
1163 constexpr std::array<int, statusAllDrives> slotToClockTable = {
1164 8, 9, 10, 11, 23, 3, 4, 5, 19, 18, 20, 13, 17, 16, 15, 14};
1165
1166 /* scan through all drives(except the status bit) and update corresponding
1167 * clock bit */
1168 for (std::size_t i = 0; i < (nvmeDriveStatus.size() - 1); i++)
1169 {
1170 if (nvmeDriveStatus.test(i))
1171 {
1172 nvmeClockStatus.set(slotToClockTable[i]);
1173 }
1174 else
1175 {
1176 nvmeClockStatus.reset(slotToClockTable[i]);
1177 }
1178 }
1179
1180 if (ioctl(hsbpFd, I2C_SLAVE_FORCE, db2000SlaveAddr) < 0)
1181 {
1182 std::cerr << "unable to set DB2000 address to " << db2000SlaveAddr
1183 << "\n";
1184 return std::nullopt;
1185 }
1186 int ret = i2c_smbus_write_byte_data(
1187 hsbpFd, db2000RegByte0,
1188 static_cast<unsigned char>(nvmeClockStatus.to_ulong()));
1189
1190 if (ret < 0)
1191 {
1192 std::cerr << "Error: unable to write data to clock register "
1193 << __FUNCTION__ << __LINE__ << "\n";
1194 return ret;
1195 }
1196
1197 ret = i2c_smbus_write_byte_data(
1198 hsbpFd, db2000RegByte1,
1199 static_cast<unsigned char>((nvmeClockStatus >> 8).to_ulong()));
1200
1201 if (ret < 0)
1202 {
1203 std::cerr << "Error: unable to write data to clock register "
1204 << __FUNCTION__ << __LINE__ << "\n";
1205 return ret;
1206 }
1207
1208 ret = i2c_smbus_write_byte_data(
1209 hsbpFd, db2000RegByte2,
1210 static_cast<unsigned char>((nvmeClockStatus >> 16).to_ulong()));
1211
1212 if (ret < 0)
1213 {
1214 std::cerr << "Error: unable to write data to clock register "
1215 << __FUNCTION__ << __LINE__ << "\n";
1216 return ret;
1217 }
1218 // Update global clock status
1219 driveClockStatus = nvmeClockStatus;
1220 driveClockStatus.set(statusAllClocksDb2000, 1);
1221 return 0;
1222}
1223
1224std::bitset<singleDriveWithStatusBit>
1225 getSingleHsbpDriveStatus(const uint8_t cpldSmbaddr)
1226{
1227 std::bitset<singleDriveWithStatusBit> singleDriveStatus;
1228
1229 // probe
1230 if (ioctl(hsbpFd, I2C_SLAVE_FORCE, cpldSmbaddr) < 0)
1231 {
1232 std::cerr << "Failed to talk to cpldSmbaddr : " << cpldSmbaddr << "\n";
1233 return singleDriveStatus;
1234 }
1235
1236 // read status of lower four drive connectivity
1237 int valueReg8 = i2c_smbus_read_byte_data(hsbpFd, hsbpCpldReg8);
1238 if (valueReg8 < 0)
1239 {
1240 std::cerr << "Error: Unable to read cpld reg 0x8 " << __FUNCTION__
1241 << __LINE__ << "\n";
1242 return singleDriveStatus;
1243 }
1244
1245 // read status of upper four drive connectivity
1246 int valueReg9 = i2c_smbus_read_byte_data(hsbpFd, hsbpCpldReg9);
1247 if (valueReg9 < 0)
1248 {
1249 std::cerr << "Error: Unable to read cpld reg 0x9 " << __FUNCTION__
1250 << __LINE__ << "\n";
1251 return singleDriveStatus;
1252 }
1253
1254 // Find drives which have NVMe drive connected
1255 for (int loop = 0; loop < (singleDriveWithStatusBit - 1); loop++)
1256 {
1257 // Check if NVME drive detected(corresponding bit numbers of reg8 and
1258 // reg9 are 1 and 0 resp)
1259 if (valueReg8 & (1U << loop))
1260 {
1261 if ((valueReg9 & (1U << loop)) == 0)
1262 {
1263 singleDriveStatus.set(loop, 1);
1264 }
1265 }
1266 }
1267
1268 // Reading successful, set the statusok bit
1269 singleDriveStatus.set(statusSingleDrive, 1);
1270 return singleDriveStatus;
1271}
1272
1273/* Try reading both HSBP and report back if atleast one of them is found to be
1274 connected. Status bit is set by the function even if one HSBP is responding
1275 */
1276std::bitset<allDrivesWithStatusBit> getCompleteDriveStatus(void)
1277{
1278 std::bitset<singleDriveWithStatusBit> singleDrvStatus;
1279 std::bitset<allDrivesWithStatusBit> currDriveStatus;
1280
1281 singleDrvStatus = getSingleHsbpDriveStatus(hsbpCpldSmbaddr1);
1282
1283 if (singleDrvStatus[statusSingleDrive] == 1)
1284 {
1285 for (int i = 0; i < maxDrivesPerHsbp; i++)
1286 {
1287 currDriveStatus[i] = singleDrvStatus[i];
1288 }
1289 // set valid bit if a single hsbp drive status is valid
1290 currDriveStatus.set(statusAllDrives);
1291 }
1292 else
1293 {
1294 currDriveStatus &= (~0xFF);
1295 }
1296
1297 singleDrvStatus = getSingleHsbpDriveStatus(hsbpCpldSmbaddr2);
1298 if (singleDrvStatus[statusSingleDrive] == 1)
1299 {
1300 for (int i = maxDrivesPerHsbp, j = 0; i < (allDrivesWithStatusBit - 1);
1301 i++, j++)
1302 {
1303 currDriveStatus[i] = singleDrvStatus[j];
1304 }
1305 // set valid bit if a single hsbp drive status is valid
1306 currDriveStatus.set(statusAllDrives);
1307 }
1308 else
1309 {
1310 currDriveStatus &= (~(0xFF << maxDrivesPerHsbp));
1311 }
1312 return currDriveStatus;
1313}
1314
1315void cpldReadingInit(void)
1316{
1317 hsbpFd = open(("/dev/i2c-" + std::to_string(hsbpI2cBus)).c_str(),
1318 O_RDWR | O_CLOEXEC);
1319 if (hsbpFd < 0)
1320 {
1321 std::cerr << "unable to open hsbpI2cBus " << hsbpI2cBus << "\n";
1322 return;
1323 }
1324
1325 std::bitset<allDrivesWithStatusBit> currDrvStatus =
1326 getCompleteDriveStatus();
1327 if (currDrvStatus[statusAllDrives] == 1)
1328 {
1329 // update global drive presence for next time comparison
1330 drivePresenceStatus = currDrvStatus;
1331 std::optional<int> updateStatus =
1332 updateClocksStatus(drivePresenceStatus);
1333 if (updateStatus.has_value())
1334 {
1335 if (updateStatus == -1)
1336 {
1337 std::cerr << "error: DB2000 register read issue "
1338 << "\n";
1339 close(hsbpFd);
1340 hsbpFd = -1;
1341 }
1342 }
1343 else
1344 {
1345 std::cerr << "error: DB2000 i2c access issue "
1346 << "\n";
1347 close(hsbpFd);
1348 hsbpFd = -1;
1349 }
1350 }
1351 else
1352 {
1353 close(hsbpFd);
1354 hsbpFd = -1;
1355 }
1356}
1357
1358// Callback handler passed to hsbpRequestAlertGpioEvents:
1359static void nvmeLvc3AlertHandler()
1360{
1361 if (hsbpFd >= 0)
1362 {
1363 gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
1364
1365 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1366 {
1367 /* Step 1: Either drive is removed or inserted; read the CPLD reg 8
1368 and 9 to determine if drive is added or removed. Need to compare
1369 current number of drives with previous state to determine
1370 it.
1371 */
1372 std::bitset<allDrivesWithStatusBit> currDrvStat =
1373 getCompleteDriveStatus();
1374 if (currDrvStat[statusAllDrives] == 1)
1375 {
1376 if (drivePresenceStatus != currDrvStat)
1377 {
1378 uint32_t tmpVar = static_cast<uint32_t>(
1379 (drivePresenceStatus ^ currDrvStat).to_ulong());
1380 uint32_t indexDrive = 0;
1381 while (tmpVar > 0)
1382 {
1383 if (tmpVar & 1)
1384 {
1385 if (drivePresenceStatus[indexDrive] == 0)
1386 {
1387 logDeviceAdded(
1388 "Drive", std::to_string(indexDrive), "N/A");
1389 }
1390 else
1391 {
1392 logDeviceRemoved(
1393 "Drive", std::to_string(indexDrive), "N/A");
1394 }
1395 }
1396 indexDrive++;
1397 tmpVar >>= 1;
1398 }
1399 // update global drive presence for next time comparison
1400 drivePresenceStatus = currDrvStat;
1401
1402 // Step 2: disable or enable the pcie clock for
1403 // corresponding drive
1404 std::optional<int> tmpUpdStatus =
1405 updateClocksStatus(currDrvStat);
1406 if (tmpUpdStatus.has_value())
1407 {
1408 if (tmpUpdStatus == -1)
1409 {
1410 std::cerr << "error: DB2000 register read issue "
1411 << "\n";
1412 close(hsbpFd);
1413 hsbpFd = -1;
1414 }
1415 }
1416 else
1417 {
1418 std::cerr << "error: DB2000 i2c access issue "
1419 << "\n";
1420 close(hsbpFd);
1421 hsbpFd = -1;
1422 }
1423 }
1424 // false alarm
1425 else
1426 {
1427 std::cerr
1428 << "False alarm detected by HSBP; no action taken \n";
1429 }
1430 }
1431 else
1432 {
1433 close(hsbpFd);
1434 hsbpFd = -1;
1435 }
1436 }
1437
1438 nvmeLvc3AlertEvent.async_wait(
1439 boost::asio::posix::stream_descriptor::wait_read,
1440 [](const boost::system::error_code ec) {
1441 if (ec)
1442 {
1443 std::cerr << "nvmealert handler error: " << ec.message()
1444 << "\n";
1445 return;
1446 }
1447 nvmeLvc3AlertHandler();
1448 });
1449 }
1450}
1451
Feist, Jamesc95cf672019-08-29 16:10:35 -07001452int main()
1453{
1454 boost::asio::steady_timer callbackTimer(io);
1455
James Feistdb2e0e72019-10-07 16:34:06 -07001456 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001457
Patrick Williamsff1c36e2022-07-22 19:26:56 -05001458 sdbusplus::bus::match_t match(
Feist, Jamesc95cf672019-08-29 16:10:35 -07001459 *conn,
1460 "type='signal',member='PropertiesChanged',arg0='" +
1461 std::string(configType) + "'",
Patrick Williamsff1c36e2022-07-22 19:26:56 -05001462 [&callbackTimer](sdbusplus::message_t&) {
Feist, Jamesc95cf672019-08-29 16:10:35 -07001463 callbackTimer.expires_after(std::chrono::seconds(2));
1464 callbackTimer.async_wait([](const boost::system::error_code ec) {
1465 if (ec == boost::asio::error::operation_aborted)
1466 {
1467 // timer was restarted
1468 return;
1469 }
1470 else if (ec)
1471 {
1472 std::cerr << "Timer error" << ec.message() << "\n";
1473 return;
1474 }
1475 populate();
1476 });
1477 });
1478
Patrick Williamsff1c36e2022-07-22 19:26:56 -05001479 sdbusplus::bus::match_t drive(
James Feist0b236ab2019-10-02 09:09:16 -07001480 *conn,
1481 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
James Feist8675a912019-10-16 14:36:58 -07001482 "Inventory.Item.NVMe'",
Patrick Williamsff1c36e2022-07-22 19:26:56 -05001483 [&callbackTimer](sdbusplus::message_t& message) {
James Feist0b236ab2019-10-02 09:09:16 -07001484 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -07001485 if (message.get_sender() == conn->get_unique_name())
1486 {
1487 return;
1488 }
James Feist0b236ab2019-10-02 09:09:16 -07001489 callbackTimer.async_wait([](const boost::system::error_code ec) {
1490 if (ec == boost::asio::error::operation_aborted)
1491 {
1492 // timer was restarted
1493 return;
1494 }
1495 else if (ec)
1496 {
1497 std::cerr << "Timer error" << ec.message() << "\n";
1498 return;
1499 }
James Feistd86629c2020-04-23 10:07:25 -07001500 updateAssets();
James Feist0b236ab2019-10-02 09:09:16 -07001501 });
1502 });
1503
Rohit Chandel52639be2021-04-14 15:10:41 +05301504 cpldReadingInit();
1505
1506 if (hsbpFd >= 0)
1507 {
1508 if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
1509 nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
1510 nvmeLvc3AlertEvent))
1511 {
1512 std::cerr << "error: Unable to monitor events on HSBP Alert line "
1513 << "\n";
1514 }
1515 }
1516
James Feist42b49c12019-10-29 15:18:43 -07001517 auto iface =
1518 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
1519 "xyz.openbmc_project.inventory.item.storage");
1520
Feist, Jamesc95cf672019-08-29 16:10:35 -07001521 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -07001522 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001523 io.run();
Rohit Chandel52639be2021-04-14 15:10:41 +05301524 close(hsbpFd);
1525 hsbpFd = -1;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001526}