blob: 85de91e6bdd780cc23f69ca68cdbcf078d2f0fc1 [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 Feist8675a912019-10-16 14:36:58 -0700216 void createAsset(
217 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700218 {
James Feist8675a912019-10-16 14:36:58 -0700219 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700220 {
221 return;
222 }
James Feist8675a912019-10-16 14:36:58 -0700223 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700224 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700225 "xyz.openbmc_project.Inventory.Decorator.Asset");
226 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700227 {
James Feist8675a912019-10-16 14:36:58 -0700228 assetIface->register_property(key, value);
James Feistd86629c2020-04-23 10:07:25 -0700229 if (key == "SerialNumber")
230 {
231 serialNumber = value;
232 }
James Feistdb2e0e72019-10-07 16:34:06 -0700233 }
James Feist8675a912019-10-16 14:36:58 -0700234 assetIface->initialize();
James Feistd86629c2020-04-23 10:07:25 -0700235 logPresent();
James Feistdb2e0e72019-10-07 16:34:06 -0700236 }
237
James Feist42b49c12019-10-29 15:18:43 -0700238 void markFailed(void)
239 {
240 // todo: maybe look this up via mapper
241 constexpr const char* globalInventoryPath =
242 "/xyz/openbmc_project/CallbackManager";
243
244 if (!isPresent)
245 {
246 return;
247 }
248
249 operationalIface->set_property("Functional", false);
250 std::vector<Association> warning = {
251 {"", "warning", globalInventoryPath}};
252 associations->set_property("Associations", warning);
James Feiste8818522019-11-04 13:36:10 -0800253 logDriveError("Drive " + std::to_string(index));
James Feist42b49c12019-10-29 15:18:43 -0700254 }
255
256 void clearFailed(void)
257 {
258 operationalIface->set_property("Functional", true);
259 associations->set_property("Associations", std::vector<Association>{});
260 }
261
James Feistd86629c2020-04-23 10:07:25 -0700262 void setPresent(bool set)
James Feiste8818522019-11-04 13:36:10 -0800263 {
264 // nvme drives get detected by their fru
James Feistd86629c2020-04-23 10:07:25 -0700265 if (set == isPresent)
James Feiste8818522019-11-04 13:36:10 -0800266 {
267 return;
268 }
269 itemIface->set_property("Present", set);
270 isPresent = set;
James Feistd86629c2020-04-23 10:07:25 -0700271 if (!isPresent)
James Feiste8818522019-11-04 13:36:10 -0800272 {
James Feistd86629c2020-04-23 10:07:25 -0700273 logDeviceRemoved("Drive", std::to_string(index), serialNumber);
274 loggedPresent = false;
James Feiste8818522019-11-04 13:36:10 -0800275 }
James Feistd86629c2020-04-23 10:07:25 -0700276 }
277
278 void logPresent()
279 {
280 if (!isPresent || loggedPresent)
James Feiste8818522019-11-04 13:36:10 -0800281 {
James Feistd86629c2020-04-23 10:07:25 -0700282 return;
James Feiste8818522019-11-04 13:36:10 -0800283 }
James Feistd86629c2020-04-23 10:07:25 -0700284 logDeviceAdded("Drive", std::to_string(index), serialNumber);
285 loggedPresent = true;
James Feiste8818522019-11-04 13:36:10 -0800286 }
287
James Feist45772222019-09-27 10:38:08 -0700288 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
289 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700290 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700291 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700292 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700293 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700294
James Feist45772222019-09-27 10:38:08 -0700295 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700296 bool isPresent;
James Feiste8818522019-11-04 13:36:10 -0800297 size_t index;
James Feistd86629c2020-04-23 10:07:25 -0700298 std::string serialNumber = "N/A";
299 bool loggedPresent = false;
James Feist45772222019-09-27 10:38:08 -0700300};
301
James Feistd86629c2020-04-23 10:07:25 -0700302struct Backplane : std::enable_shared_from_this<Backplane>
Feist, Jamesc95cf672019-08-29 16:10:35 -0700303{
304
James Feist45772222019-09-27 10:38:08 -0700305 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
306 const std::string& nameIn) :
307 bus(busIn),
308 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feistd86629c2020-04-23 10:07:25 -0700309 timer(boost::asio::steady_timer(io)),
James Feist8675a912019-10-16 14:36:58 -0700310 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700311 {
312 }
James Feistd0d36f12019-11-21 10:19:44 -0800313 void populateAsset(const std::string& rootPath, const std::string& busname)
314 {
315 conn->async_method_call(
316 [assetIface{assetInterface}, hsbpIface{hsbpItemIface}](
317 const boost::system::error_code ec,
318 const boost::container::flat_map<
319 std::string, std::variant<std::string>>& values) mutable {
320 if (ec)
321 {
322 std::cerr
323 << "Error getting asset tag from HSBP configuration\n";
324
325 return;
326 }
327 assetIface = objServer.add_interface(
328 hsbpIface->get_object_path(), assetTag);
329 for (const auto& [key, value] : values)
330 {
331 const std::string* ptr = std::get_if<std::string>(&value);
332 if (ptr == nullptr)
333 {
334 std::cerr << key << " Invalid type!\n";
335 continue;
336 }
337 assetIface->register_property(key, *ptr);
338 }
339 assetIface->initialize();
340 },
341 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
342 assetTag);
343 }
344
345 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700346 {
James Feist09dd2312019-10-09 09:29:03 -0700347 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
348 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700349 if (file < 0)
350 {
351 std::cerr << "unable to open bus " << bus << "\n";
352 return;
353 }
354
355 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
356 {
357 std::cerr << "unable to set address to " << address << "\n";
358 return;
359 }
360
James Feist45772222019-09-27 10:38:08 -0700361 if (!getPresent())
362 {
363 std::cerr << "Cannot detect CPLD\n";
364 return;
365 }
366
367 getBootVer(bootVer);
368 getFPGAVer(fpgaVer);
369 getSecurityRev(securityRev);
370 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700371 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700372 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700373 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700374 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700375 hsbpItemIface->register_property("PrettyName", name);
376 hsbpItemIface->initialize();
377
James Feistd0d36f12019-11-21 10:19:44 -0800378 storageInterface = objServer.add_interface(
379 hsbpItemIface->get_object_path(),
380 "xyz.openbmc_project.Inventory.Item.StorageController");
381 storageInterface->initialize();
382
James Feist45772222019-09-27 10:38:08 -0700383 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800384 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700385 "xyz.openbmc_project.Software.Version");
386 versionIface->register_property("Version", zeroPad(bootVer) + "." +
387 zeroPad(fpgaVer) + "." +
388 zeroPad(securityRev));
389 versionIface->register_property(
390 "Purpose",
391 std::string(
392 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
393 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000394
395 auto activationIface =
396 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
397 "xyz.openbmc_project.Software.Activation");
398
399 activationIface->register_property(
400 "Activation",
401 std::string(
402 "xyz.openbmc_project.Software.Activation.Activations.Active"));
403 activationIface->register_property(
404 "RequestedActivation",
405 std::string("xyz.openbmc_project.Software.Activation."
406 "RequestedActivations.None"));
407
408 activationIface->initialize();
409
James Feist45772222019-09-27 10:38:08 -0700410 getPresence(presence);
411 getIFDET(ifdet);
412
James Feistd0d36f12019-11-21 10:19:44 -0800413 populateAsset(rootPath, busname);
414
James Feist45772222019-09-27 10:38:08 -0700415 createDrives();
416
417 runTimer();
418 }
419
420 void runTimer()
421 {
James Feistd86629c2020-04-23 10:07:25 -0700422 timer.expires_after(std::chrono::seconds(scanRateSeconds));
423 timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
424 boost::system::error_code ec) {
425 auto self = weak.lock();
426 if (!self)
427 {
428 return;
429 }
James Feist45772222019-09-27 10:38:08 -0700430 if (ec == boost::asio::error::operation_aborted)
431 {
432 // we're being destroyed
433 return;
434 }
435 else if (ec)
436 {
437 std::cerr << "timer error " << ec.message() << "\n";
438 return;
439 }
James Feist9f6565d2019-10-09 13:15:13 -0700440
441 if (!isPowerOn())
442 {
443 // can't access hsbp when power is off
James Feistd86629c2020-04-23 10:07:25 -0700444 self->runTimer();
James Feist9f6565d2019-10-09 13:15:13 -0700445 return;
446 }
James Feist45772222019-09-27 10:38:08 -0700447
James Feistd86629c2020-04-23 10:07:25 -0700448 self->getPresence(self->presence);
449 self->getIFDET(self->ifdet);
450 self->getFailed(self->failed);
451 self->getRebuild(self->rebuilding);
James Feist45772222019-09-27 10:38:08 -0700452
James Feistd86629c2020-04-23 10:07:25 -0700453 self->updateDrives();
454 self->runTimer();
James Feist45772222019-09-27 10:38:08 -0700455 });
456 }
457
458 void createDrives()
459 {
460 uint8_t nvme = ifdet ^ presence;
461 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700462 {
James Feist45772222019-09-27 10:38:08 -0700463 bool isNvme = nvme & (1 << ii);
464 bool isPresent = isNvme || (presence & (1 << ii));
465 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700466 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700467
468 // +1 to convert from 0 based to 1 based
469 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700470 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
471 isNvme, isRebuilding);
472 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
473 drive.itemIface->get_object_path(), ii, file));
474 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700475 }
476 }
477
James Feist45772222019-09-27 10:38:08 -0700478 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700479 {
James Feist45772222019-09-27 10:38:08 -0700480
481 uint8_t nvme = ifdet ^ presence;
James Feist42b49c12019-10-29 15:18:43 -0700482 size_t ii = 0;
483
484 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700485 {
James Feist45772222019-09-27 10:38:08 -0700486 bool isNvme = nvme & (1 << ii);
487 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700488 bool isFailed = !isPresent || (failed & (1 << ii));
489 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700490
James Feist42b49c12019-10-29 15:18:43 -0700491 it->isNvme = isNvme;
James Feistda0c35f2020-02-03 10:16:13 -0800492
James Feistd86629c2020-04-23 10:07:25 -0700493 it->setPresent(isPresent);
James Feistda0c35f2020-02-03 10:16:13 -0800494
James Feist42b49c12019-10-29 15:18:43 -0700495 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
496 if (isFailed || isRebuilding)
497 {
498 it->markFailed();
499 }
500 else
501 {
502 it->clearFailed();
503 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700504 }
James Feist45772222019-09-27 10:38:08 -0700505 }
506
507 bool getPresent()
508 {
509 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700510 return present;
511 }
James Feist45772222019-09-27 10:38:08 -0700512
513 bool getTypeID(uint8_t& val)
514 {
515 constexpr uint8_t addr = 2;
516 int ret = i2c_smbus_read_byte_data(file, addr);
517 if (ret < 0)
518 {
519 std::cerr << "Error " << __FUNCTION__ << "\n";
520 return false;
521 }
522 val = static_cast<uint8_t>(ret);
523 return true;
524 }
525
526 bool getBootVer(uint8_t& val)
527 {
528 constexpr uint8_t addr = 3;
529 int ret = i2c_smbus_read_byte_data(file, addr);
530 if (ret < 0)
531 {
532 std::cerr << "Error " << __FUNCTION__ << "\n";
533 return false;
534 }
535 val = static_cast<uint8_t>(ret);
536 return true;
537 }
538
539 bool getFPGAVer(uint8_t& val)
540 {
541 constexpr uint8_t addr = 4;
542 int ret = i2c_smbus_read_byte_data(file, addr);
543 if (ret < 0)
544 {
545 std::cerr << "Error " << __FUNCTION__ << "\n";
546 return false;
547 }
548 val = static_cast<uint8_t>(ret);
549 return true;
550 }
551
552 bool getSecurityRev(uint8_t& val)
553 {
554 constexpr uint8_t addr = 5;
555 int ret = i2c_smbus_read_byte_data(file, addr);
556 if (ret < 0)
557 {
558 std::cerr << "Error " << __FUNCTION__ << "\n";
559 return false;
560 }
561 val = static_cast<uint8_t>(ret);
562 return true;
563 }
564
565 bool getPresence(uint8_t& val)
566 {
567 // NVMe drives do not assert PRSNTn, and as such do not get reported as
568 // PRESENT in this register
569
570 constexpr uint8_t addr = 8;
571
572 int ret = i2c_smbus_read_byte_data(file, addr);
573 if (ret < 0)
574 {
575 std::cerr << "Error " << __FUNCTION__ << "\n";
576 return false;
577 }
578 // presence is inverted
579 val = static_cast<uint8_t>(~ret);
580 return true;
581 }
582
583 bool getIFDET(uint8_t& val)
584 {
585 // This register is a bitmap of parallel GPIO pins connected to the
586 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
587 // IFDETn low when they are inserted into the HSBP.This register, in
588 // combination with the PRESENCE register, are used by the BMC to detect
589 // the presence of NVMe drives.
590
591 constexpr uint8_t addr = 9;
592
593 int ret = i2c_smbus_read_byte_data(file, addr);
594 if (ret < 0)
595 {
596 std::cerr << "Error " << __FUNCTION__ << "\n";
597 return false;
598 }
599 // ifdet is inverted
600 val = static_cast<uint8_t>(~ret);
601 return true;
602 }
603
604 bool getFailed(uint8_t& val)
605 {
606 constexpr uint8_t addr = 0xC;
607 int ret = i2c_smbus_read_byte_data(file, addr);
608 if (ret < 0)
609 {
610 std::cerr << "Error " << __FUNCTION__ << "\n";
611 return false;
612 }
613 val = static_cast<uint8_t>(ret);
614 return true;
615 }
616
617 bool getRebuild(uint8_t& val)
618 {
619 constexpr uint8_t addr = 0xD;
620 int ret = i2c_smbus_read_byte_data(file, addr);
621 if (ret < 0)
622 {
James Feistd86629c2020-04-23 10:07:25 -0700623 std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
624 << "\n";
James Feist45772222019-09-27 10:38:08 -0700625 return false;
626 }
627 val = static_cast<uint8_t>(ret);
628 return true;
629 }
630
James Feistd86629c2020-04-23 10:07:25 -0700631 void logDrivePresence()
632 {
633 for (auto& drive : drives)
634 {
635 drive.logPresent();
636 }
637 }
638
James Feist09dd2312019-10-09 09:29:03 -0700639 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700640 {
641 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700642 objServer.remove_interface(versionIface);
James Feistd86629c2020-04-23 10:07:25 -0700643 timer.cancel();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700644 if (file >= 0)
645 {
646 close(file);
647 }
648 }
649
650 size_t bus;
651 size_t address;
James Feist45772222019-09-27 10:38:08 -0700652 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700653 std::string name;
James Feistd86629c2020-04-23 10:07:25 -0700654 boost::asio::steady_timer timer;
James Feist45772222019-09-27 10:38:08 -0700655 bool present = false;
656 uint8_t typeId = 0;
657 uint8_t bootVer = 0;
658 uint8_t fpgaVer = 0;
659 uint8_t securityRev = 0;
660 uint8_t funSupported = 0;
661 uint8_t presence = 0;
662 uint8_t ifdet = 0;
663 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700664 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700665
666 int file = -1;
667
Feist, Jamesc95cf672019-08-29 16:10:35 -0700668 std::string type;
669
670 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700671 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -0800672 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
673 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
James Feist45772222019-09-27 10:38:08 -0700674
James Feist42b49c12019-10-29 15:18:43 -0700675 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700676 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -0700677 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700678};
679
James Feistd86629c2020-04-23 10:07:25 -0700680std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
James Feist42b49c12019-10-29 15:18:43 -0700681std::list<Drive> ownerlessDrives; // drives without a backplane
Feist, Jamesc95cf672019-08-29 16:10:35 -0700682
James Feist8675a912019-10-16 14:36:58 -0700683static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -0700684{
James Feist8675a912019-10-16 14:36:58 -0700685 size_t count = 0;
686 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -0700687 {
James Feistd86629c2020-04-23 10:07:25 -0700688 count += backplane->drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700689 }
James Feist8675a912019-10-16 14:36:58 -0700690 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700691}
692
James Feist8675a912019-10-16 14:36:58 -0700693void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -0700694{
James Feist8675a912019-10-16 14:36:58 -0700695 static constexpr const char* nvmeType =
696 "xyz.openbmc_project.Inventory.Item.NVMe";
James Feist0b236ab2019-10-02 09:09:16 -0700697
698 conn->async_method_call(
699 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
700 if (ec)
701 {
702 std::cerr << "Error contacting mapper " << ec.message() << "\n";
703 return;
704 }
James Feist8675a912019-10-16 14:36:58 -0700705
706 // drives may get an owner during this, or we might disover more
707 // drives
708 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -0700709 for (const auto& [path, objDict] : subtree)
710 {
711 if (objDict.empty())
712 {
713 continue;
714 }
715
716 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700717 // we export this interface too
718 if (owner == busName)
719 {
720 continue;
721 }
James Feist8675a912019-10-16 14:36:58 -0700722 if (std::find(objDict.begin()->second.begin(),
723 objDict.begin()->second.end(),
724 assetTag) == objDict.begin()->second.end())
725 {
726 // no asset tag to associate to
727 continue;
728 }
729
James Feistd86629c2020-04-23 10:07:25 -0700730 auto callback = std::make_shared<std::function<void()>>([]() {
731 for (auto [_, backplane] : backplanes)
732 {
733 backplane->logDrivePresence();
734 }
735 });
736
James Feist0b236ab2019-10-02 09:09:16 -0700737 conn->async_method_call(
James Feistd86629c2020-04-23 10:07:25 -0700738 [callback, path](
739 const boost::system::error_code ec2,
740 const boost::container::flat_map<
741 std::string, std::variant<uint64_t, std::string>>&
742 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700743 if (ec2)
744 {
745 std::cerr << "Error Getting Config "
746 << ec2.message() << " " << __FUNCTION__
747 << "\n";
748 return;
749 }
750 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -0700751
James Feist8675a912019-10-16 14:36:58 -0700752 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -0700753 {
754 std::cerr << "Illegal interface at " << path
755 << "\n";
756 return;
757 }
758
James Feist8675a912019-10-16 14:36:58 -0700759 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -0700760 size_t muxBus = static_cast<size_t>(
761 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -0700762 std::filesystem::path muxPath =
763 "/sys/bus/i2c/devices/i2c-" +
764 std::to_string(muxBus) + "/mux_device";
765 if (!std::filesystem::is_symlink(muxPath))
766 {
767 std::cerr << path << " mux does not exist\n";
768 return;
769 }
770
James Feist8675a912019-10-16 14:36:58 -0700771 // we should be getting something of the form 7-0052
772 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -0700773 std::string fname =
774 std::filesystem::read_symlink(muxPath).filename();
775 auto findDash = fname.find('-');
776
777 if (findDash == std::string::npos ||
778 findDash + 1 >= fname.size())
779 {
780 std::cerr << path << " mux path invalid\n";
781 return;
782 }
783
784 std::string busStr = fname.substr(0, findDash);
785 std::string muxStr = fname.substr(findDash + 1);
786
787 size_t bus = static_cast<size_t>(std::stoi(busStr));
788 size_t addr =
789 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -0700790 size_t muxIndex = 0;
791
792 // find the channel of the mux the drive is on
793 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
794 std::to_string(muxBus) +
795 "/name");
796 if (!nameFile)
797 {
798 std::cerr << "Unable to open name file of bus "
799 << muxBus << "\n";
800 return;
801 }
802
803 std::string nameStr;
804 std::getline(nameFile, nameStr);
805
806 // file is of the form "i2c-4-mux (chan_id 1)", get chan
807 // assume single digit chan
808 const std::string prefix = "chan_id ";
809 size_t findId = nameStr.find(prefix);
810 if (findId == std::string::npos ||
811 findId + 1 >= nameStr.size())
812 {
813 std::cerr << "Illegal name file on bus " << muxBus
814 << "\n";
815 }
816
817 std::string indexStr =
818 nameStr.substr(findId + prefix.size(), 1);
819
820 size_t driveIndex = std::stoi(indexStr);
821
James Feist0b236ab2019-10-02 09:09:16 -0700822 Backplane* parent = nullptr;
823 for (auto& [name, backplane] : backplanes)
824 {
James Feist8675a912019-10-16 14:36:58 -0700825 muxIndex = 0;
James Feistd86629c2020-04-23 10:07:25 -0700826 for (const Mux& mux : *(backplane->muxes))
James Feist0b236ab2019-10-02 09:09:16 -0700827 {
828 if (bus == mux.bus && addr == mux.address)
829 {
James Feistd86629c2020-04-23 10:07:25 -0700830 parent = backplane.get();
James Feist0b236ab2019-10-02 09:09:16 -0700831 break;
832 }
James Feist8675a912019-10-16 14:36:58 -0700833 muxIndex += mux.channels;
James Feist0b236ab2019-10-02 09:09:16 -0700834 }
835 }
James Feist8675a912019-10-16 14:36:58 -0700836 boost::container::flat_map<std::string, std::string>
837 assetInventory;
838 const std::array<const char*, 4> assetKeys = {
839 "PartNumber", "SerialNumber", "Manufacturer",
840 "Model"};
841 for (const auto& [key, value] : values)
842 {
843 if (std::find(assetKeys.begin(), assetKeys.end(),
844 key) == assetKeys.end())
845 {
846 continue;
847 }
848 assetInventory[key] = std::get<std::string>(value);
849 }
850
851 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -0700852 if (parent == nullptr)
853 {
James Feist8675a912019-10-16 14:36:58 -0700854 auto& drive = ownerlessDrives.emplace_back(
855 getDriveCount() + 1, true, true, true, false);
856 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700857 return;
858 }
James Feist8675a912019-10-16 14:36:58 -0700859
860 driveIndex += muxIndex;
861
James Feist0b236ab2019-10-02 09:09:16 -0700862 if (parent->drives.size() <= driveIndex)
863 {
James Feist0b236ab2019-10-02 09:09:16 -0700864 std::cerr << "Illegal drive index at " << path
865 << " " << driveIndex << "\n";
866 return;
867 }
James Feist42b49c12019-10-29 15:18:43 -0700868 auto it = parent->drives.begin();
869 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -0700870
James Feist42b49c12019-10-29 15:18:43 -0700871 it->createAsset(assetInventory);
James Feistd86629c2020-04-23 10:07:25 -0700872 if (callback.use_count() == 1)
873 {
874 (*callback)();
875 }
James Feist0b236ab2019-10-02 09:09:16 -0700876 },
877 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -0700878 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -0700879 }
880 },
881 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
James Feist8675a912019-10-16 14:36:58 -0700882 0, std::array<const char*, 1>{nvmeType});
James Feist0b236ab2019-10-02 09:09:16 -0700883}
884
James Feist8675a912019-10-16 14:36:58 -0700885void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -0700886 std::string& rootPath)
887{
888 const static std::array<const std::string, 4> muxTypes = {
889 "xyz.openbmc_project.Configuration.PCA9543Mux",
890 "xyz.openbmc_project.Configuration.PCA9544Mux",
891 "xyz.openbmc_project.Configuration.PCA9545Mux",
892 "xyz.openbmc_project.Configuration.PCA9546Mux"};
893 conn->async_method_call(
894 [muxes](const boost::system::error_code ec,
895 const GetSubTreeType& subtree) {
896 if (ec)
897 {
898 std::cerr << "Error contacting mapper " << ec.message() << "\n";
899 return;
900 }
901 std::shared_ptr<std::function<void()>> callback =
902 std::make_shared<std::function<void()>>(
James Feist8675a912019-10-16 14:36:58 -0700903 []() { updateAssets(); });
904 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -0700905 for (const auto& [path, objDict] : subtree)
906 {
907 if (objDict.empty() || objDict.begin()->second.empty())
908 {
909 continue;
910 }
911
912 const std::string& owner = objDict.begin()->first;
913 const std::vector<std::string>& interfaces =
914 objDict.begin()->second;
915
916 const std::string* interface = nullptr;
917 for (const std::string& iface : interfaces)
918 {
919 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
920 muxTypes.end())
921 {
922 interface = &iface;
923 break;
924 }
925 }
926 if (interface == nullptr)
927 {
928 std::cerr << "Cannot get mux type\n";
929 continue;
930 }
931
932 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700933 [path, muxes, callback, index](
James Feist0b236ab2019-10-02 09:09:16 -0700934 const boost::system::error_code ec2,
935 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -0700936 std::string,
937 std::variant<uint64_t, std::vector<std::string>>>&
938 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700939 if (ec2)
940 {
941 std::cerr << "Error Getting Config "
942 << ec2.message() << " " << __FUNCTION__
943 << "\n";
944 return;
945 }
946 auto findBus = values.find("Bus");
947 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -0700948 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -0700949 if (findBus == values.end() ||
950 findAddress == values.end())
951 {
952 std::cerr << "Illegal configuration at " << path
953 << "\n";
954 return;
955 }
956 size_t bus = static_cast<size_t>(
957 std::get<uint64_t>(findBus->second));
958 size_t address = static_cast<size_t>(
959 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -0700960 std::vector<std::string> channels =
961 std::get<std::vector<std::string>>(
962 findChannelNames->second);
963 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -0700964 if (callback.use_count() == 1)
965 {
966 (*callback)();
967 }
968 },
969 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
970 *interface);
James Feist8675a912019-10-16 14:36:58 -0700971 index++;
James Feist0b236ab2019-10-02 09:09:16 -0700972 }
973 },
974 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
975 rootPath, 1, muxTypes);
976}
977
Feist, Jamesc95cf672019-08-29 16:10:35 -0700978void populate()
979{
980 conn->async_method_call(
981 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
982 if (ec)
983 {
984 std::cerr << "Error contacting mapper " << ec.message() << "\n";
985 return;
986 }
987 for (const auto& [path, objDict] : subtree)
988 {
989 if (objDict.empty())
990 {
991 continue;
992 }
993
994 const std::string& owner = objDict.begin()->first;
995 conn->async_method_call(
James Feistd0d36f12019-11-21 10:19:44 -0800996 [path, owner](const boost::system::error_code ec2,
997 const boost::container::flat_map<
998 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -0700999 if (ec2)
1000 {
1001 std::cerr << "Error Getting Config "
1002 << ec2.message() << "\n";
1003 return;
1004 }
1005 backplanes.clear();
1006 std::optional<size_t> bus;
1007 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -07001008 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -07001009 std::optional<std::string> name;
1010 for (const auto& [key, value] : resp)
1011 {
1012 if (key == "Bus")
1013 {
1014 bus = std::get<uint64_t>(value);
1015 }
1016 else if (key == "Address")
1017 {
1018 address = std::get<uint64_t>(value);
1019 }
James Feist45772222019-09-27 10:38:08 -07001020 else if (key == "Index")
1021 {
1022 backplaneIndex = std::get<uint64_t>(value);
1023 }
Feist, Jamesc95cf672019-08-29 16:10:35 -07001024 else if (key == "Name")
1025 {
1026 name = std::get<std::string>(value);
1027 }
1028 }
James Feist45772222019-09-27 10:38:08 -07001029 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001030 {
1031 std::cerr << "Illegal configuration at " << path
1032 << "\n";
1033 return;
1034 }
James Feist0b236ab2019-10-02 09:09:16 -07001035 std::string parentPath =
1036 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001037 const auto& [backplane, status] = backplanes.emplace(
James Feistd86629c2020-04-23 10:07:25 -07001038 *name, std::make_shared<Backplane>(
1039 *bus, *address, *backplaneIndex, *name));
1040 backplane->second->run(parentPath, owner);
1041 populateMuxes(backplane->second->muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001042 },
1043 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1044 configType);
1045 }
1046 },
1047 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1048 0, std::array<const char*, 1>{configType});
1049}
1050
1051int main()
1052{
1053 boost::asio::steady_timer callbackTimer(io);
1054
James Feistdb2e0e72019-10-07 16:34:06 -07001055 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001056
1057 sdbusplus::bus::match::match match(
1058 *conn,
1059 "type='signal',member='PropertiesChanged',arg0='" +
1060 std::string(configType) + "'",
1061 [&callbackTimer](sdbusplus::message::message&) {
1062 callbackTimer.expires_after(std::chrono::seconds(2));
1063 callbackTimer.async_wait([](const boost::system::error_code ec) {
1064 if (ec == boost::asio::error::operation_aborted)
1065 {
1066 // timer was restarted
1067 return;
1068 }
1069 else if (ec)
1070 {
1071 std::cerr << "Timer error" << ec.message() << "\n";
1072 return;
1073 }
1074 populate();
1075 });
1076 });
1077
James Feist0b236ab2019-10-02 09:09:16 -07001078 sdbusplus::bus::match::match drive(
1079 *conn,
1080 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
James Feist8675a912019-10-16 14:36:58 -07001081 "Inventory.Item.NVMe'",
James Feistdb2e0e72019-10-07 16:34:06 -07001082 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -07001083 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -07001084 if (message.get_sender() == conn->get_unique_name())
1085 {
1086 return;
1087 }
James Feist0b236ab2019-10-02 09:09:16 -07001088 callbackTimer.async_wait([](const boost::system::error_code ec) {
1089 if (ec == boost::asio::error::operation_aborted)
1090 {
1091 // timer was restarted
1092 return;
1093 }
1094 else if (ec)
1095 {
1096 std::cerr << "Timer error" << ec.message() << "\n";
1097 return;
1098 }
James Feistd86629c2020-04-23 10:07:25 -07001099 updateAssets();
James Feist0b236ab2019-10-02 09:09:16 -07001100 });
1101 });
1102
James Feist42b49c12019-10-29 15:18:43 -07001103 auto iface =
1104 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
1105 "xyz.openbmc_project.inventory.item.storage");
1106
Feist, Jamesc95cf672019-08-29 16:10:35 -07001107 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -07001108 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001109 io.run();
1110}