blob: 4780d18ae4805cd228f69ef614481cbcecd55784 [file] [log] [blame]
Feist, Jamesc95cf672019-08-29 16:10:35 -07001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "utils.hpp"
18
19#include <boost/algorithm/string/replace.hpp>
20#include <boost/asio/steady_timer.hpp>
James Feist8675a912019-10-16 14:36:58 -070021#include <boost/container/flat_set.hpp>
James Feist0b236ab2019-10-02 09:09:16 -070022#include <filesystem>
James Feist8675a912019-10-16 14:36:58 -070023#include <fstream>
Feist, Jamesc95cf672019-08-29 16:10:35 -070024#include <iostream>
25#include <sdbusplus/asio/connection.hpp>
26#include <sdbusplus/asio/object_server.hpp>
27#include <sdbusplus/bus/match.hpp>
28#include <string>
James Feist45772222019-09-27 10:38:08 -070029#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070030
31extern "C" {
32#include <i2c/smbus.h>
33#include <linux/i2c-dev.h>
34}
35
36constexpr const char* configType =
37 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
James Feistdb2e0e72019-10-07 16:34:06 -070038constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
Feist, Jamesc95cf672019-08-29 16:10:35 -070039
James Feist45772222019-09-27 10:38:08 -070040constexpr size_t scanRateSeconds = 5;
41constexpr size_t maxDrives = 8; // only 1 byte alloted
42
Feist, Jamesc95cf672019-08-29 16:10:35 -070043boost::asio::io_context io;
44auto conn = std::make_shared<sdbusplus::asio::connection>(io);
45sdbusplus::asio::object_server objServer(conn);
46
James Feist45772222019-09-27 10:38:08 -070047static std::string zeroPad(const uint8_t val)
48{
49 std::ostringstream version;
50 version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val);
51 return version.str();
52}
53
James Feist0b236ab2019-10-02 09:09:16 -070054struct Mux
55{
James Feist8675a912019-10-16 14:36:58 -070056 Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
57 bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
James Feist0b236ab2019-10-02 09:09:16 -070058 {
59 }
60 size_t bus;
61 size_t address;
James Feist8675a912019-10-16 14:36:58 -070062 size_t channels;
63 size_t index;
64
65 // to sort in the flat set
66 bool operator<(const Mux& rhs) const
67 {
68 return index < rhs.index;
69 }
James Feist0b236ab2019-10-02 09:09:16 -070070};
James Feist09dd2312019-10-09 09:29:03 -070071
72enum class BlinkPattern : uint8_t
73{
74 off = 0x0,
75 error = 0x2,
76 terminate = 0x3
77};
78
79struct Led : std::enable_shared_from_this<Led>
80{
81 // led pattern addresses start at 0x10
82 Led(const std::string& path, size_t index, int fd) :
83 address(static_cast<uint8_t>(index + 0x10)), file(fd),
84 ledInterface(objServer.add_interface(path, ledGroup::interface))
85 {
86 if (index >= maxDrives)
87 {
88 throw std::runtime_error("Invalid drive index");
89 }
90
91 if (!set(BlinkPattern::off))
92 {
93 std::cerr << "Cannot initialize LED " << path << "\n";
94 }
95 }
96
97 // this has to be called outside the constructor for shared_from_this to
98 // work
99 void createInterface(void)
100 {
101 std::shared_ptr<Led> self = shared_from_this();
102
103 ledInterface->register_property(
104 ledGroup::asserted, false, [self](const bool req, bool& val) {
105 if (req == val)
106 {
107 return 1;
108 }
James Feist9f6565d2019-10-09 13:15:13 -0700109
110 if (!isPowerOn())
111 {
112 std::cerr << "Can't change blink state when power is off\n";
113 throw std::runtime_error(
114 "Can't change blink state when power is off");
115 }
James Feist09dd2312019-10-09 09:29:03 -0700116 BlinkPattern pattern =
117 req ? BlinkPattern::error : BlinkPattern::terminate;
118 if (!self->set(pattern))
119 {
James Feist9f6565d2019-10-09 13:15:13 -0700120 std::cerr << "Can't change blink pattern\n";
James Feist09dd2312019-10-09 09:29:03 -0700121 throw std::runtime_error("Cannot set blink pattern");
122 }
123 val = req;
124 return 1;
125 });
126 ledInterface->initialize();
127 }
128
129 virtual ~Led()
130 {
131 objServer.remove_interface(ledInterface);
132 }
133
134 bool set(BlinkPattern pattern)
135 {
136 int ret = i2c_smbus_write_byte_data(file, address,
137 static_cast<uint8_t>(pattern));
138 return ret >= 0;
139 }
140
141 uint8_t address;
142 int file;
143 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
144};
145
James Feist45772222019-09-27 10:38:08 -0700146struct Drive
147{
James Feist42b49c12019-10-29 15:18:43 -0700148 Drive(size_t driveIndex, bool present, bool isOperational, bool nvme,
James Feist244f3232019-09-27 15:15:14 -0700149 bool rebuilding) :
James Feist42b49c12019-10-29 15:18:43 -0700150 isNvme(nvme),
James Feiste8818522019-11-04 13:36:10 -0800151 isPresent(present), index(driveIndex)
James Feist45772222019-09-27 10:38:08 -0700152 {
153 constexpr const char* basePath =
154 "/xyz/openbmc_project/inventory/item/drive/Drive_";
155 itemIface = objServer.add_interface(
156 basePath + std::to_string(driveIndex), inventory::interface);
157 itemIface->register_property("Present", isPresent);
158 itemIface->register_property("PrettyName",
159 "Drive " + std::to_string(driveIndex));
160 itemIface->initialize();
161 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700162 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700163 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feist42b49c12019-10-29 15:18:43 -0700164
165 operationalIface->register_property(
166 "Functional", isOperational,
167 [this](const bool req, bool& property) {
168 if (!isPresent)
169 {
170 return 0;
171 }
172 if (property == req)
173 {
174 return 1;
175 }
176 property = req;
177 if (req)
178 {
179 clearFailed();
180 return 1;
181 }
182 markFailed();
183 return 1;
184 });
185
James Feist45772222019-09-27 10:38:08 -0700186 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700187 rebuildingIface = objServer.add_interface(
188 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
189 rebuildingIface->register_property("Rebuilding", rebuilding);
190 rebuildingIface->initialize();
James Feist8675a912019-10-16 14:36:58 -0700191 driveIface =
192 objServer.add_interface(itemIface->get_object_path(),
193 "xyz.openbmc_project.Inventory.Item.Drive");
194 driveIface->initialize();
James Feist42b49c12019-10-29 15:18:43 -0700195 associations = objServer.add_interface(itemIface->get_object_path(),
196 association::interface);
197 associations->register_property("Associations",
198 std::vector<Association>{});
199 associations->initialize();
200
201 if (isPresent && (!isOperational || rebuilding))
202 {
203 markFailed();
204 }
James Feist45772222019-09-27 10:38:08 -0700205 }
James Feist09dd2312019-10-09 09:29:03 -0700206 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700207 {
208 objServer.remove_interface(itemIface);
209 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700210 objServer.remove_interface(rebuildingIface);
James Feist8675a912019-10-16 14:36:58 -0700211 objServer.remove_interface(assetIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700212 objServer.remove_interface(driveIface);
James Feist42b49c12019-10-29 15:18:43 -0700213 objServer.remove_interface(associations);
James Feist0b236ab2019-10-02 09:09:16 -0700214 }
215
James Feistc66735b2020-07-17 13:51:21 -0700216 void removeAsset()
217 {
218 objServer.remove_interface(assetIface);
219 assetIface = nullptr;
220 }
221
James Feist8675a912019-10-16 14:36:58 -0700222 void createAsset(
223 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700224 {
James Feist8675a912019-10-16 14:36:58 -0700225 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700226 {
227 return;
228 }
James Feist8675a912019-10-16 14:36:58 -0700229 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700230 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700231 "xyz.openbmc_project.Inventory.Decorator.Asset");
232 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700233 {
James Feist8675a912019-10-16 14:36:58 -0700234 assetIface->register_property(key, value);
James Feistd86629c2020-04-23 10:07:25 -0700235 if (key == "SerialNumber")
236 {
237 serialNumber = value;
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700238 serialNumberInitialized = true;
James Feistd86629c2020-04-23 10:07:25 -0700239 }
James Feistdb2e0e72019-10-07 16:34:06 -0700240 }
James Feist8675a912019-10-16 14:36:58 -0700241 assetIface->initialize();
James Feistdb2e0e72019-10-07 16:34:06 -0700242 }
243
James Feist42b49c12019-10-29 15:18:43 -0700244 void markFailed(void)
245 {
246 // todo: maybe look this up via mapper
247 constexpr const char* globalInventoryPath =
248 "/xyz/openbmc_project/CallbackManager";
249
250 if (!isPresent)
251 {
252 return;
253 }
254
255 operationalIface->set_property("Functional", false);
256 std::vector<Association> warning = {
257 {"", "warning", globalInventoryPath}};
258 associations->set_property("Associations", warning);
James Feiste8818522019-11-04 13:36:10 -0800259 logDriveError("Drive " + std::to_string(index));
James Feist42b49c12019-10-29 15:18:43 -0700260 }
261
262 void clearFailed(void)
263 {
264 operationalIface->set_property("Functional", true);
265 associations->set_property("Associations", std::vector<Association>{});
266 }
267
James Feistd86629c2020-04-23 10:07:25 -0700268 void setPresent(bool set)
James Feiste8818522019-11-04 13:36:10 -0800269 {
270 // nvme drives get detected by their fru
James Feistd86629c2020-04-23 10:07:25 -0700271 if (set == isPresent)
James Feiste8818522019-11-04 13:36:10 -0800272 {
273 return;
274 }
275 itemIface->set_property("Present", set);
276 isPresent = set;
James Feistd86629c2020-04-23 10:07:25 -0700277 }
278
279 void logPresent()
280 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700281 if (isNvme && !serialNumberInitialized)
James Feiste8818522019-11-04 13:36:10 -0800282 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700283 // wait until NVMe asset is updated to include the serial number
284 // from the NVMe drive
James Feistd86629c2020-04-23 10:07:25 -0700285 return;
James Feiste8818522019-11-04 13:36:10 -0800286 }
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700287
288 if (!isPresent && loggedPresent)
289 {
290 loggedPresent = false;
291 logDeviceRemoved("Drive", std::to_string(index), serialNumber);
292 serialNumber = "N/A";
293 serialNumberInitialized = false;
James Feistc66735b2020-07-17 13:51:21 -0700294 removeAsset();
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700295 }
296 else if (isPresent && !loggedPresent)
297 {
298 loggedPresent = true;
299 logDeviceAdded("Drive", std::to_string(index), serialNumber);
300 }
James Feiste8818522019-11-04 13:36:10 -0800301 }
302
James Feist45772222019-09-27 10:38:08 -0700303 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
304 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700305 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700306 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700307 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700308 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700309
James Feist45772222019-09-27 10:38:08 -0700310 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700311 bool isPresent;
James Feiste8818522019-11-04 13:36:10 -0800312 size_t index;
James Feistd86629c2020-04-23 10:07:25 -0700313 std::string serialNumber = "N/A";
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700314 bool serialNumberInitialized = false;
James Feistd86629c2020-04-23 10:07:25 -0700315 bool loggedPresent = false;
James Feist45772222019-09-27 10:38:08 -0700316};
317
James Feistd86629c2020-04-23 10:07:25 -0700318struct Backplane : std::enable_shared_from_this<Backplane>
Feist, Jamesc95cf672019-08-29 16:10:35 -0700319{
320
James Feist45772222019-09-27 10:38:08 -0700321 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
322 const std::string& nameIn) :
323 bus(busIn),
324 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feistd86629c2020-04-23 10:07:25 -0700325 timer(boost::asio::steady_timer(io)),
James Feist8675a912019-10-16 14:36:58 -0700326 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700327 {
328 }
James Feistd0d36f12019-11-21 10:19:44 -0800329 void populateAsset(const std::string& rootPath, const std::string& busname)
330 {
331 conn->async_method_call(
332 [assetIface{assetInterface}, hsbpIface{hsbpItemIface}](
333 const boost::system::error_code ec,
334 const boost::container::flat_map<
335 std::string, std::variant<std::string>>& values) mutable {
336 if (ec)
337 {
338 std::cerr
339 << "Error getting asset tag from HSBP configuration\n";
340
341 return;
342 }
343 assetIface = objServer.add_interface(
344 hsbpIface->get_object_path(), assetTag);
345 for (const auto& [key, value] : values)
346 {
347 const std::string* ptr = std::get_if<std::string>(&value);
348 if (ptr == nullptr)
349 {
350 std::cerr << key << " Invalid type!\n";
351 continue;
352 }
353 assetIface->register_property(key, *ptr);
354 }
355 assetIface->initialize();
356 },
357 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
358 assetTag);
359 }
360
361 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700362 {
James Feist09dd2312019-10-09 09:29:03 -0700363 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
364 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700365 if (file < 0)
366 {
367 std::cerr << "unable to open bus " << bus << "\n";
368 return;
369 }
370
371 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
372 {
373 std::cerr << "unable to set address to " << address << "\n";
374 return;
375 }
376
James Feist45772222019-09-27 10:38:08 -0700377 if (!getPresent())
378 {
379 std::cerr << "Cannot detect CPLD\n";
380 return;
381 }
382
383 getBootVer(bootVer);
384 getFPGAVer(fpgaVer);
385 getSecurityRev(securityRev);
386 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700387 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700388 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700389 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700390 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700391 hsbpItemIface->register_property("PrettyName", name);
392 hsbpItemIface->initialize();
393
James Feistd0d36f12019-11-21 10:19:44 -0800394 storageInterface = objServer.add_interface(
395 hsbpItemIface->get_object_path(),
396 "xyz.openbmc_project.Inventory.Item.StorageController");
397 storageInterface->initialize();
398
James Feist45772222019-09-27 10:38:08 -0700399 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800400 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700401 "xyz.openbmc_project.Software.Version");
402 versionIface->register_property("Version", zeroPad(bootVer) + "." +
403 zeroPad(fpgaVer) + "." +
404 zeroPad(securityRev));
405 versionIface->register_property(
406 "Purpose",
407 std::string(
408 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
409 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000410
411 auto activationIface =
412 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
413 "xyz.openbmc_project.Software.Activation");
414
415 activationIface->register_property(
416 "Activation",
417 std::string(
418 "xyz.openbmc_project.Software.Activation.Activations.Active"));
419 activationIface->register_property(
420 "RequestedActivation",
421 std::string("xyz.openbmc_project.Software.Activation."
422 "RequestedActivations.None"));
423
424 activationIface->initialize();
425
James Feist45772222019-09-27 10:38:08 -0700426 getPresence(presence);
427 getIFDET(ifdet);
428
James Feistd0d36f12019-11-21 10:19:44 -0800429 populateAsset(rootPath, busname);
430
James Feist45772222019-09-27 10:38:08 -0700431 createDrives();
432
433 runTimer();
434 }
435
436 void runTimer()
437 {
James Feistd86629c2020-04-23 10:07:25 -0700438 timer.expires_after(std::chrono::seconds(scanRateSeconds));
439 timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
440 boost::system::error_code ec) {
441 auto self = weak.lock();
442 if (!self)
443 {
444 return;
445 }
James Feist45772222019-09-27 10:38:08 -0700446 if (ec == boost::asio::error::operation_aborted)
447 {
448 // we're being destroyed
449 return;
450 }
451 else if (ec)
452 {
453 std::cerr << "timer error " << ec.message() << "\n";
454 return;
455 }
James Feist9f6565d2019-10-09 13:15:13 -0700456
457 if (!isPowerOn())
458 {
459 // can't access hsbp when power is off
James Feistd86629c2020-04-23 10:07:25 -0700460 self->runTimer();
James Feist9f6565d2019-10-09 13:15:13 -0700461 return;
462 }
James Feist45772222019-09-27 10:38:08 -0700463
James Feistd86629c2020-04-23 10:07:25 -0700464 self->getPresence(self->presence);
465 self->getIFDET(self->ifdet);
466 self->getFailed(self->failed);
467 self->getRebuild(self->rebuilding);
James Feist45772222019-09-27 10:38:08 -0700468
James Feistd86629c2020-04-23 10:07:25 -0700469 self->updateDrives();
470 self->runTimer();
James Feist45772222019-09-27 10:38:08 -0700471 });
472 }
473
474 void createDrives()
475 {
James Feist45772222019-09-27 10:38:08 -0700476 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700477 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700478 uint8_t driveSlot = (1 << ii);
479 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
480 bool isPresent = isNvme || (presence & driveSlot);
481 bool isFailed = !isPresent || failed & driveSlot;
482 bool isRebuilding = !isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -0700483
484 // +1 to convert from 0 based to 1 based
485 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700486 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
487 isNvme, isRebuilding);
488 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
489 drive.itemIface->get_object_path(), ii, file));
490 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700491 }
492 }
493
James Feist45772222019-09-27 10:38:08 -0700494 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700495 {
James Feist42b49c12019-10-29 15:18:43 -0700496 size_t ii = 0;
497
498 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700499 {
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700500 uint8_t driveSlot = (1 << ii);
501 bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
502 bool isPresent = isNvme || (presence & driveSlot);
503 bool isFailed = !isPresent || (failed & driveSlot);
504 bool isRebuilding = isPresent && (rebuilding & driveSlot);
James Feist45772222019-09-27 10:38:08 -0700505
James Feist42b49c12019-10-29 15:18:43 -0700506 it->isNvme = isNvme;
James Feistd86629c2020-04-23 10:07:25 -0700507 it->setPresent(isPresent);
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700508 it->logPresent();
James Feistda0c35f2020-02-03 10:16:13 -0800509
James Feist42b49c12019-10-29 15:18:43 -0700510 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
511 if (isFailed || isRebuilding)
512 {
513 it->markFailed();
514 }
515 else
516 {
517 it->clearFailed();
518 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700519 }
James Feist45772222019-09-27 10:38:08 -0700520 }
521
522 bool getPresent()
523 {
524 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700525 return present;
526 }
James Feist45772222019-09-27 10:38:08 -0700527
528 bool getTypeID(uint8_t& val)
529 {
530 constexpr uint8_t addr = 2;
531 int ret = i2c_smbus_read_byte_data(file, addr);
532 if (ret < 0)
533 {
534 std::cerr << "Error " << __FUNCTION__ << "\n";
535 return false;
536 }
537 val = static_cast<uint8_t>(ret);
538 return true;
539 }
540
541 bool getBootVer(uint8_t& val)
542 {
543 constexpr uint8_t addr = 3;
544 int ret = i2c_smbus_read_byte_data(file, addr);
545 if (ret < 0)
546 {
547 std::cerr << "Error " << __FUNCTION__ << "\n";
548 return false;
549 }
550 val = static_cast<uint8_t>(ret);
551 return true;
552 }
553
554 bool getFPGAVer(uint8_t& val)
555 {
556 constexpr uint8_t addr = 4;
557 int ret = i2c_smbus_read_byte_data(file, addr);
558 if (ret < 0)
559 {
560 std::cerr << "Error " << __FUNCTION__ << "\n";
561 return false;
562 }
563 val = static_cast<uint8_t>(ret);
564 return true;
565 }
566
567 bool getSecurityRev(uint8_t& val)
568 {
569 constexpr uint8_t addr = 5;
570 int ret = i2c_smbus_read_byte_data(file, addr);
571 if (ret < 0)
572 {
573 std::cerr << "Error " << __FUNCTION__ << "\n";
574 return false;
575 }
576 val = static_cast<uint8_t>(ret);
577 return true;
578 }
579
580 bool getPresence(uint8_t& val)
581 {
582 // NVMe drives do not assert PRSNTn, and as such do not get reported as
583 // PRESENT in this register
584
585 constexpr uint8_t addr = 8;
586
587 int ret = i2c_smbus_read_byte_data(file, addr);
588 if (ret < 0)
589 {
590 std::cerr << "Error " << __FUNCTION__ << "\n";
591 return false;
592 }
593 // presence is inverted
594 val = static_cast<uint8_t>(~ret);
595 return true;
596 }
597
598 bool getIFDET(uint8_t& val)
599 {
600 // This register is a bitmap of parallel GPIO pins connected to the
601 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
602 // IFDETn low when they are inserted into the HSBP.This register, in
603 // combination with the PRESENCE register, are used by the BMC to detect
604 // the presence of NVMe drives.
605
606 constexpr uint8_t addr = 9;
607
608 int ret = i2c_smbus_read_byte_data(file, addr);
609 if (ret < 0)
610 {
611 std::cerr << "Error " << __FUNCTION__ << "\n";
612 return false;
613 }
614 // ifdet is inverted
615 val = static_cast<uint8_t>(~ret);
616 return true;
617 }
618
619 bool getFailed(uint8_t& val)
620 {
621 constexpr uint8_t addr = 0xC;
622 int ret = i2c_smbus_read_byte_data(file, addr);
623 if (ret < 0)
624 {
625 std::cerr << "Error " << __FUNCTION__ << "\n";
626 return false;
627 }
628 val = static_cast<uint8_t>(ret);
629 return true;
630 }
631
632 bool getRebuild(uint8_t& val)
633 {
634 constexpr uint8_t addr = 0xD;
635 int ret = i2c_smbus_read_byte_data(file, addr);
636 if (ret < 0)
637 {
James Feistd86629c2020-04-23 10:07:25 -0700638 std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
639 << "\n";
James Feist45772222019-09-27 10:38:08 -0700640 return false;
641 }
642 val = static_cast<uint8_t>(ret);
643 return true;
644 }
645
James Feist09dd2312019-10-09 09:29:03 -0700646 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700647 {
648 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700649 objServer.remove_interface(versionIface);
James Feistd86629c2020-04-23 10:07:25 -0700650 timer.cancel();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700651 if (file >= 0)
652 {
653 close(file);
654 }
655 }
656
657 size_t bus;
658 size_t address;
James Feist45772222019-09-27 10:38:08 -0700659 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700660 std::string name;
James Feistd86629c2020-04-23 10:07:25 -0700661 boost::asio::steady_timer timer;
James Feist45772222019-09-27 10:38:08 -0700662 bool present = false;
663 uint8_t typeId = 0;
664 uint8_t bootVer = 0;
665 uint8_t fpgaVer = 0;
666 uint8_t securityRev = 0;
667 uint8_t funSupported = 0;
668 uint8_t presence = 0;
669 uint8_t ifdet = 0;
670 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700671 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700672
673 int file = -1;
674
Feist, Jamesc95cf672019-08-29 16:10:35 -0700675 std::string type;
676
677 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700678 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -0800679 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
680 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
James Feist45772222019-09-27 10:38:08 -0700681
James Feist42b49c12019-10-29 15:18:43 -0700682 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700683 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -0700684 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700685};
686
James Feistd86629c2020-04-23 10:07:25 -0700687std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
James Feist42b49c12019-10-29 15:18:43 -0700688std::list<Drive> ownerlessDrives; // drives without a backplane
Feist, Jamesc95cf672019-08-29 16:10:35 -0700689
James Feist8675a912019-10-16 14:36:58 -0700690static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -0700691{
James Feist8675a912019-10-16 14:36:58 -0700692 size_t count = 0;
693 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -0700694 {
James Feistd86629c2020-04-23 10:07:25 -0700695 count += backplane->drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700696 }
James Feist8675a912019-10-16 14:36:58 -0700697 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700698}
699
James Feist8675a912019-10-16 14:36:58 -0700700void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -0700701{
James Feist8675a912019-10-16 14:36:58 -0700702 static constexpr const char* nvmeType =
703 "xyz.openbmc_project.Inventory.Item.NVMe";
James Feist0b236ab2019-10-02 09:09:16 -0700704
705 conn->async_method_call(
706 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
707 if (ec)
708 {
709 std::cerr << "Error contacting mapper " << ec.message() << "\n";
710 return;
711 }
James Feist8675a912019-10-16 14:36:58 -0700712
713 // drives may get an owner during this, or we might disover more
714 // drives
715 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -0700716 for (const auto& [path, objDict] : subtree)
717 {
718 if (objDict.empty())
719 {
720 continue;
721 }
722
723 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700724 // we export this interface too
725 if (owner == busName)
726 {
727 continue;
728 }
James Feist8675a912019-10-16 14:36:58 -0700729 if (std::find(objDict.begin()->second.begin(),
730 objDict.begin()->second.end(),
731 assetTag) == objDict.begin()->second.end())
732 {
733 // no asset tag to associate to
734 continue;
735 }
736
James Feist0b236ab2019-10-02 09:09:16 -0700737 conn->async_method_call(
Johnathan Mantey7045b4b2020-06-19 09:24:49 -0700738 [path](const boost::system::error_code ec2,
739 const boost::container::flat_map<
740 std::string,
741 std::variant<uint64_t, std::string>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700742 if (ec2)
743 {
744 std::cerr << "Error Getting Config "
745 << ec2.message() << " " << __FUNCTION__
746 << "\n";
747 return;
748 }
749 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -0700750
James Feist8675a912019-10-16 14:36:58 -0700751 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -0700752 {
753 std::cerr << "Illegal interface at " << path
754 << "\n";
755 return;
756 }
757
James Feist8675a912019-10-16 14:36:58 -0700758 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -0700759 size_t muxBus = static_cast<size_t>(
760 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -0700761 std::filesystem::path muxPath =
762 "/sys/bus/i2c/devices/i2c-" +
763 std::to_string(muxBus) + "/mux_device";
764 if (!std::filesystem::is_symlink(muxPath))
765 {
766 std::cerr << path << " mux does not exist\n";
767 return;
768 }
769
James Feist8675a912019-10-16 14:36:58 -0700770 // we should be getting something of the form 7-0052
771 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -0700772 std::string fname =
773 std::filesystem::read_symlink(muxPath).filename();
774 auto findDash = fname.find('-');
775
776 if (findDash == std::string::npos ||
777 findDash + 1 >= fname.size())
778 {
779 std::cerr << path << " mux path invalid\n";
780 return;
781 }
782
783 std::string busStr = fname.substr(0, findDash);
784 std::string muxStr = fname.substr(findDash + 1);
785
786 size_t bus = static_cast<size_t>(std::stoi(busStr));
787 size_t addr =
788 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -0700789 size_t muxIndex = 0;
790
791 // find the channel of the mux the drive is on
792 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
793 std::to_string(muxBus) +
794 "/name");
795 if (!nameFile)
796 {
797 std::cerr << "Unable to open name file of bus "
798 << muxBus << "\n";
799 return;
800 }
801
802 std::string nameStr;
803 std::getline(nameFile, nameStr);
804
805 // file is of the form "i2c-4-mux (chan_id 1)", get chan
806 // assume single digit chan
807 const std::string prefix = "chan_id ";
808 size_t findId = nameStr.find(prefix);
809 if (findId == std::string::npos ||
810 findId + 1 >= nameStr.size())
811 {
812 std::cerr << "Illegal name file on bus " << muxBus
813 << "\n";
814 }
815
816 std::string indexStr =
817 nameStr.substr(findId + prefix.size(), 1);
818
819 size_t driveIndex = std::stoi(indexStr);
820
James Feist0b236ab2019-10-02 09:09:16 -0700821 Backplane* parent = nullptr;
822 for (auto& [name, backplane] : backplanes)
823 {
James Feist8675a912019-10-16 14:36:58 -0700824 muxIndex = 0;
James Feistd86629c2020-04-23 10:07:25 -0700825 for (const Mux& mux : *(backplane->muxes))
James Feist0b236ab2019-10-02 09:09:16 -0700826 {
827 if (bus == mux.bus && addr == mux.address)
828 {
James Feistd86629c2020-04-23 10:07:25 -0700829 parent = backplane.get();
James Feist0b236ab2019-10-02 09:09:16 -0700830 break;
831 }
James Feist8675a912019-10-16 14:36:58 -0700832 muxIndex += mux.channels;
James Feist0b236ab2019-10-02 09:09:16 -0700833 }
834 }
James Feist8675a912019-10-16 14:36:58 -0700835 boost::container::flat_map<std::string, std::string>
836 assetInventory;
837 const std::array<const char*, 4> assetKeys = {
838 "PartNumber", "SerialNumber", "Manufacturer",
839 "Model"};
840 for (const auto& [key, value] : values)
841 {
842 if (std::find(assetKeys.begin(), assetKeys.end(),
843 key) == assetKeys.end())
844 {
845 continue;
846 }
847 assetInventory[key] = std::get<std::string>(value);
848 }
849
850 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -0700851 if (parent == nullptr)
852 {
James Feist8675a912019-10-16 14:36:58 -0700853 auto& drive = ownerlessDrives.emplace_back(
854 getDriveCount() + 1, true, true, true, false);
855 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700856 return;
857 }
James Feist8675a912019-10-16 14:36:58 -0700858
859 driveIndex += muxIndex;
860
James Feist0b236ab2019-10-02 09:09:16 -0700861 if (parent->drives.size() <= driveIndex)
862 {
James Feist0b236ab2019-10-02 09:09:16 -0700863 std::cerr << "Illegal drive index at " << path
864 << " " << driveIndex << "\n";
865 return;
866 }
James Feist42b49c12019-10-29 15:18:43 -0700867 auto it = parent->drives.begin();
868 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -0700869
James Feist42b49c12019-10-29 15:18:43 -0700870 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700871 },
872 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -0700873 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -0700874 }
875 },
876 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
James Feist8675a912019-10-16 14:36:58 -0700877 0, std::array<const char*, 1>{nvmeType});
James Feist0b236ab2019-10-02 09:09:16 -0700878}
879
James Feist8675a912019-10-16 14:36:58 -0700880void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -0700881 std::string& rootPath)
882{
883 const static std::array<const std::string, 4> muxTypes = {
884 "xyz.openbmc_project.Configuration.PCA9543Mux",
885 "xyz.openbmc_project.Configuration.PCA9544Mux",
886 "xyz.openbmc_project.Configuration.PCA9545Mux",
887 "xyz.openbmc_project.Configuration.PCA9546Mux"};
888 conn->async_method_call(
889 [muxes](const boost::system::error_code ec,
890 const GetSubTreeType& subtree) {
891 if (ec)
892 {
893 std::cerr << "Error contacting mapper " << ec.message() << "\n";
894 return;
895 }
896 std::shared_ptr<std::function<void()>> callback =
897 std::make_shared<std::function<void()>>(
James Feist8675a912019-10-16 14:36:58 -0700898 []() { updateAssets(); });
899 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -0700900 for (const auto& [path, objDict] : subtree)
901 {
902 if (objDict.empty() || objDict.begin()->second.empty())
903 {
904 continue;
905 }
906
907 const std::string& owner = objDict.begin()->first;
908 const std::vector<std::string>& interfaces =
909 objDict.begin()->second;
910
911 const std::string* interface = nullptr;
912 for (const std::string& iface : interfaces)
913 {
914 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
915 muxTypes.end())
916 {
917 interface = &iface;
918 break;
919 }
920 }
921 if (interface == nullptr)
922 {
923 std::cerr << "Cannot get mux type\n";
924 continue;
925 }
926
927 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700928 [path, muxes, callback, index](
James Feist0b236ab2019-10-02 09:09:16 -0700929 const boost::system::error_code ec2,
930 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -0700931 std::string,
932 std::variant<uint64_t, std::vector<std::string>>>&
933 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700934 if (ec2)
935 {
936 std::cerr << "Error Getting Config "
937 << ec2.message() << " " << __FUNCTION__
938 << "\n";
939 return;
940 }
941 auto findBus = values.find("Bus");
942 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -0700943 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -0700944 if (findBus == values.end() ||
945 findAddress == values.end())
946 {
947 std::cerr << "Illegal configuration at " << path
948 << "\n";
949 return;
950 }
951 size_t bus = static_cast<size_t>(
952 std::get<uint64_t>(findBus->second));
953 size_t address = static_cast<size_t>(
954 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -0700955 std::vector<std::string> channels =
956 std::get<std::vector<std::string>>(
957 findChannelNames->second);
958 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -0700959 if (callback.use_count() == 1)
960 {
961 (*callback)();
962 }
963 },
964 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
965 *interface);
James Feist8675a912019-10-16 14:36:58 -0700966 index++;
James Feist0b236ab2019-10-02 09:09:16 -0700967 }
968 },
969 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
970 rootPath, 1, muxTypes);
971}
972
Feist, Jamesc95cf672019-08-29 16:10:35 -0700973void populate()
974{
975 conn->async_method_call(
976 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
977 if (ec)
978 {
979 std::cerr << "Error contacting mapper " << ec.message() << "\n";
980 return;
981 }
982 for (const auto& [path, objDict] : subtree)
983 {
984 if (objDict.empty())
985 {
986 continue;
987 }
988
989 const std::string& owner = objDict.begin()->first;
990 conn->async_method_call(
James Feistd0d36f12019-11-21 10:19:44 -0800991 [path, owner](const boost::system::error_code ec2,
992 const boost::container::flat_map<
993 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -0700994 if (ec2)
995 {
996 std::cerr << "Error Getting Config "
997 << ec2.message() << "\n";
998 return;
999 }
1000 backplanes.clear();
1001 std::optional<size_t> bus;
1002 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -07001003 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001004 std::optional<std::string> name;
1005 for (const auto& [key, value] : resp)
1006 {
1007 if (key == "Bus")
1008 {
1009 bus = std::get<uint64_t>(value);
1010 }
1011 else if (key == "Address")
1012 {
1013 address = std::get<uint64_t>(value);
1014 }
James Feist45772222019-09-27 10:38:08 -07001015 else if (key == "Index")
1016 {
1017 backplaneIndex = std::get<uint64_t>(value);
1018 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001019 else if (key == "Name")
1020 {
1021 name = std::get<std::string>(value);
1022 }
1023 }
James Feist45772222019-09-27 10:38:08 -07001024 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001025 {
1026 std::cerr << "Illegal configuration at " << path
1027 << "\n";
1028 return;
1029 }
James Feist0b236ab2019-10-02 09:09:16 -07001030 std::string parentPath =
1031 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001032 const auto& [backplane, status] = backplanes.emplace(
James Feistd86629c2020-04-23 10:07:25 -07001033 *name, std::make_shared<Backplane>(
1034 *bus, *address, *backplaneIndex, *name));
1035 backplane->second->run(parentPath, owner);
1036 populateMuxes(backplane->second->muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001037 },
1038 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1039 configType);
1040 }
1041 },
1042 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1043 0, std::array<const char*, 1>{configType});
1044}
1045
1046int main()
1047{
1048 boost::asio::steady_timer callbackTimer(io);
1049
James Feistdb2e0e72019-10-07 16:34:06 -07001050 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001051
1052 sdbusplus::bus::match::match match(
1053 *conn,
1054 "type='signal',member='PropertiesChanged',arg0='" +
1055 std::string(configType) + "'",
1056 [&callbackTimer](sdbusplus::message::message&) {
1057 callbackTimer.expires_after(std::chrono::seconds(2));
1058 callbackTimer.async_wait([](const boost::system::error_code ec) {
1059 if (ec == boost::asio::error::operation_aborted)
1060 {
1061 // timer was restarted
1062 return;
1063 }
1064 else if (ec)
1065 {
1066 std::cerr << "Timer error" << ec.message() << "\n";
1067 return;
1068 }
1069 populate();
1070 });
1071 });
1072
James Feist0b236ab2019-10-02 09:09:16 -07001073 sdbusplus::bus::match::match drive(
1074 *conn,
1075 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
James Feist8675a912019-10-16 14:36:58 -07001076 "Inventory.Item.NVMe'",
James Feistdb2e0e72019-10-07 16:34:06 -07001077 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -07001078 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -07001079 if (message.get_sender() == conn->get_unique_name())
1080 {
1081 return;
1082 }
James Feist0b236ab2019-10-02 09:09:16 -07001083 callbackTimer.async_wait([](const boost::system::error_code ec) {
1084 if (ec == boost::asio::error::operation_aborted)
1085 {
1086 // timer was restarted
1087 return;
1088 }
1089 else if (ec)
1090 {
1091 std::cerr << "Timer error" << ec.message() << "\n";
1092 return;
1093 }
James Feistd86629c2020-04-23 10:07:25 -07001094 updateAssets();
James Feist0b236ab2019-10-02 09:09:16 -07001095 });
1096 });
1097
James Feist42b49c12019-10-29 15:18:43 -07001098 auto iface =
1099 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
1100 "xyz.openbmc_project.inventory.item.storage");
1101
Feist, Jamesc95cf672019-08-29 16:10:35 -07001102 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -07001103 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001104 io.run();
1105}