blob: 56d0d240815728016ec57d8a7e094f28c8738822 [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);
James Feiste8818522019-11-04 13:36:10 -0800158 if (isPresent && !isNvme)
159 {
160 // nvme drives get detected by their fru
161 logDeviceAdded("Drive", std::to_string(index), "N/A");
162 }
James Feist45772222019-09-27 10:38:08 -0700163 itemIface->register_property("PrettyName",
164 "Drive " + std::to_string(driveIndex));
165 itemIface->initialize();
166 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700167 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700168 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feist42b49c12019-10-29 15:18:43 -0700169
170 operationalIface->register_property(
171 "Functional", isOperational,
172 [this](const bool req, bool& property) {
173 if (!isPresent)
174 {
175 return 0;
176 }
177 if (property == req)
178 {
179 return 1;
180 }
181 property = req;
182 if (req)
183 {
184 clearFailed();
185 return 1;
186 }
187 markFailed();
188 return 1;
189 });
190
James Feist45772222019-09-27 10:38:08 -0700191 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700192 rebuildingIface = objServer.add_interface(
193 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
194 rebuildingIface->register_property("Rebuilding", rebuilding);
195 rebuildingIface->initialize();
James Feist8675a912019-10-16 14:36:58 -0700196 driveIface =
197 objServer.add_interface(itemIface->get_object_path(),
198 "xyz.openbmc_project.Inventory.Item.Drive");
199 driveIface->initialize();
James Feist42b49c12019-10-29 15:18:43 -0700200 associations = objServer.add_interface(itemIface->get_object_path(),
201 association::interface);
202 associations->register_property("Associations",
203 std::vector<Association>{});
204 associations->initialize();
205
206 if (isPresent && (!isOperational || rebuilding))
207 {
208 markFailed();
209 }
James Feist45772222019-09-27 10:38:08 -0700210 }
James Feist09dd2312019-10-09 09:29:03 -0700211 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700212 {
213 objServer.remove_interface(itemIface);
214 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700215 objServer.remove_interface(rebuildingIface);
James Feist8675a912019-10-16 14:36:58 -0700216 objServer.remove_interface(assetIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700217 objServer.remove_interface(driveIface);
James Feist42b49c12019-10-29 15:18:43 -0700218 objServer.remove_interface(associations);
James Feist0b236ab2019-10-02 09:09:16 -0700219 }
220
James Feist8675a912019-10-16 14:36:58 -0700221 void createAsset(
222 const boost::container::flat_map<std::string, std::string>& data)
James Feist0b236ab2019-10-02 09:09:16 -0700223 {
James Feist8675a912019-10-16 14:36:58 -0700224 if (assetIface != nullptr)
James Feist0b236ab2019-10-02 09:09:16 -0700225 {
226 return;
227 }
James Feist8675a912019-10-16 14:36:58 -0700228 assetIface = objServer.add_interface(
James Feist0b236ab2019-10-02 09:09:16 -0700229 itemIface->get_object_path(),
James Feist8675a912019-10-16 14:36:58 -0700230 "xyz.openbmc_project.Inventory.Decorator.Asset");
231 for (const auto& [key, value] : data)
James Feistdb2e0e72019-10-07 16:34:06 -0700232 {
James Feist8675a912019-10-16 14:36:58 -0700233 assetIface->register_property(key, value);
James Feistdb2e0e72019-10-07 16:34:06 -0700234 }
James Feist8675a912019-10-16 14:36:58 -0700235 assetIface->initialize();
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 Feiste8818522019-11-04 13:36:10 -0800262 void setPresent(bool set)
263 {
264 // nvme drives get detected by their fru
265 if (isNvme || set == isPresent)
266 {
267 return;
268 }
269 itemIface->set_property("Present", set);
270 isPresent = set;
271 if (isPresent)
272 {
273 logDeviceAdded("Drive", std::to_string(index), "N/A");
274 }
275 else
276 {
277 logDeviceRemoved("Drive", std::to_string(index), "N/A");
278 }
279 }
280
James Feist45772222019-09-27 10:38:08 -0700281 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
282 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700283 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700284 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700285 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700286 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700287
James Feist45772222019-09-27 10:38:08 -0700288 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700289 bool isPresent;
James Feiste8818522019-11-04 13:36:10 -0800290 size_t index;
James Feist45772222019-09-27 10:38:08 -0700291};
292
Feist, Jamesc95cf672019-08-29 16:10:35 -0700293struct Backplane
294{
295
James Feist45772222019-09-27 10:38:08 -0700296 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
297 const std::string& nameIn) :
298 bus(busIn),
299 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feist0b236ab2019-10-02 09:09:16 -0700300 timer(std::make_shared<boost::asio::steady_timer>(io)),
James Feist8675a912019-10-16 14:36:58 -0700301 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700302 {
303 }
James Feistd0d36f12019-11-21 10:19:44 -0800304 void populateAsset(const std::string& rootPath, const std::string& busname)
305 {
306 conn->async_method_call(
307 [assetIface{assetInterface}, hsbpIface{hsbpItemIface}](
308 const boost::system::error_code ec,
309 const boost::container::flat_map<
310 std::string, std::variant<std::string>>& values) mutable {
311 if (ec)
312 {
313 std::cerr
314 << "Error getting asset tag from HSBP configuration\n";
315
316 return;
317 }
318 assetIface = objServer.add_interface(
319 hsbpIface->get_object_path(), assetTag);
320 for (const auto& [key, value] : values)
321 {
322 const std::string* ptr = std::get_if<std::string>(&value);
323 if (ptr == nullptr)
324 {
325 std::cerr << key << " Invalid type!\n";
326 continue;
327 }
328 assetIface->register_property(key, *ptr);
329 }
330 assetIface->initialize();
331 },
332 busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
333 assetTag);
334 }
335
336 void run(const std::string& rootPath, const std::string& busname)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700337 {
James Feist09dd2312019-10-09 09:29:03 -0700338 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
339 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700340 if (file < 0)
341 {
342 std::cerr << "unable to open bus " << bus << "\n";
343 return;
344 }
345
346 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
347 {
348 std::cerr << "unable to set address to " << address << "\n";
349 return;
350 }
351
James Feist45772222019-09-27 10:38:08 -0700352 if (!getPresent())
353 {
354 std::cerr << "Cannot detect CPLD\n";
355 return;
356 }
357
358 getBootVer(bootVer);
359 getFPGAVer(fpgaVer);
360 getSecurityRev(securityRev);
361 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700362 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700363 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700364 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700365 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700366 hsbpItemIface->register_property("PrettyName", name);
367 hsbpItemIface->initialize();
368
James Feistd0d36f12019-11-21 10:19:44 -0800369 storageInterface = objServer.add_interface(
370 hsbpItemIface->get_object_path(),
371 "xyz.openbmc_project.Inventory.Item.StorageController");
372 storageInterface->initialize();
373
James Feist45772222019-09-27 10:38:08 -0700374 versionIface =
James Feiste6db7832020-01-06 14:20:09 -0800375 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
James Feist45772222019-09-27 10:38:08 -0700376 "xyz.openbmc_project.Software.Version");
377 versionIface->register_property("Version", zeroPad(bootVer) + "." +
378 zeroPad(fpgaVer) + "." +
379 zeroPad(securityRev));
380 versionIface->register_property(
381 "Purpose",
382 std::string(
383 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
384 versionIface->initialize();
jayaprakash Mutyala05f8d572020-01-31 15:56:52 +0000385
386 auto activationIface =
387 objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
388 "xyz.openbmc_project.Software.Activation");
389
390 activationIface->register_property(
391 "Activation",
392 std::string(
393 "xyz.openbmc_project.Software.Activation.Activations.Active"));
394 activationIface->register_property(
395 "RequestedActivation",
396 std::string("xyz.openbmc_project.Software.Activation."
397 "RequestedActivations.None"));
398
399 activationIface->initialize();
400
James Feist45772222019-09-27 10:38:08 -0700401 getPresence(presence);
402 getIFDET(ifdet);
403
James Feistd0d36f12019-11-21 10:19:44 -0800404 populateAsset(rootPath, busname);
405
James Feist45772222019-09-27 10:38:08 -0700406 createDrives();
407
408 runTimer();
409 }
410
411 void runTimer()
412 {
413 timer->expires_after(std::chrono::seconds(scanRateSeconds));
414 timer->async_wait([this](boost::system::error_code ec) {
415 if (ec == boost::asio::error::operation_aborted)
416 {
417 // we're being destroyed
418 return;
419 }
420 else if (ec)
421 {
422 std::cerr << "timer error " << ec.message() << "\n";
423 return;
424 }
James Feist9f6565d2019-10-09 13:15:13 -0700425
426 if (!isPowerOn())
427 {
428 // can't access hsbp when power is off
429 runTimer();
430 return;
431 }
James Feist45772222019-09-27 10:38:08 -0700432 uint8_t curPresence = 0;
433 uint8_t curIFDET = 0;
434 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700435 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700436
437 getPresence(curPresence);
438 getIFDET(curIFDET);
439 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700440 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700441
442 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700443 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700444 {
445 presence = curPresence;
446 ifdet = curIFDET;
447 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700448 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700449 updateDrives();
450 }
451 runTimer();
452 });
453 }
454
455 void createDrives()
456 {
457 uint8_t nvme = ifdet ^ presence;
458 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700459 {
James Feist45772222019-09-27 10:38:08 -0700460 bool isNvme = nvme & (1 << ii);
461 bool isPresent = isNvme || (presence & (1 << ii));
462 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700463 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700464
465 // +1 to convert from 0 based to 1 based
466 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700467 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
468 isNvme, isRebuilding);
469 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
470 drive.itemIface->get_object_path(), ii, file));
471 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700472 }
473 }
474
James Feist45772222019-09-27 10:38:08 -0700475 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700476 {
James Feist45772222019-09-27 10:38:08 -0700477
478 uint8_t nvme = ifdet ^ presence;
James Feist42b49c12019-10-29 15:18:43 -0700479 size_t ii = 0;
480
481 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700482 {
James Feist45772222019-09-27 10:38:08 -0700483 bool isNvme = nvme & (1 << ii);
484 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700485 bool isFailed = !isPresent || (failed & (1 << ii));
486 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700487
James Feist42b49c12019-10-29 15:18:43 -0700488 it->isNvme = isNvme;
James Feiste8818522019-11-04 13:36:10 -0800489 it->setPresent(isPresent);
James Feist42b49c12019-10-29 15:18:43 -0700490 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
491 if (isFailed || isRebuilding)
492 {
493 it->markFailed();
494 }
495 else
496 {
497 it->clearFailed();
498 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700499 }
James Feist45772222019-09-27 10:38:08 -0700500 }
501
502 bool getPresent()
503 {
504 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700505 return present;
506 }
James Feist45772222019-09-27 10:38:08 -0700507
508 bool getTypeID(uint8_t& val)
509 {
510 constexpr uint8_t addr = 2;
511 int ret = i2c_smbus_read_byte_data(file, addr);
512 if (ret < 0)
513 {
514 std::cerr << "Error " << __FUNCTION__ << "\n";
515 return false;
516 }
517 val = static_cast<uint8_t>(ret);
518 return true;
519 }
520
521 bool getBootVer(uint8_t& val)
522 {
523 constexpr uint8_t addr = 3;
524 int ret = i2c_smbus_read_byte_data(file, addr);
525 if (ret < 0)
526 {
527 std::cerr << "Error " << __FUNCTION__ << "\n";
528 return false;
529 }
530 val = static_cast<uint8_t>(ret);
531 return true;
532 }
533
534 bool getFPGAVer(uint8_t& val)
535 {
536 constexpr uint8_t addr = 4;
537 int ret = i2c_smbus_read_byte_data(file, addr);
538 if (ret < 0)
539 {
540 std::cerr << "Error " << __FUNCTION__ << "\n";
541 return false;
542 }
543 val = static_cast<uint8_t>(ret);
544 return true;
545 }
546
547 bool getSecurityRev(uint8_t& val)
548 {
549 constexpr uint8_t addr = 5;
550 int ret = i2c_smbus_read_byte_data(file, addr);
551 if (ret < 0)
552 {
553 std::cerr << "Error " << __FUNCTION__ << "\n";
554 return false;
555 }
556 val = static_cast<uint8_t>(ret);
557 return true;
558 }
559
560 bool getPresence(uint8_t& val)
561 {
562 // NVMe drives do not assert PRSNTn, and as such do not get reported as
563 // PRESENT in this register
564
565 constexpr uint8_t addr = 8;
566
567 int ret = i2c_smbus_read_byte_data(file, addr);
568 if (ret < 0)
569 {
570 std::cerr << "Error " << __FUNCTION__ << "\n";
571 return false;
572 }
573 // presence is inverted
574 val = static_cast<uint8_t>(~ret);
575 return true;
576 }
577
578 bool getIFDET(uint8_t& val)
579 {
580 // This register is a bitmap of parallel GPIO pins connected to the
581 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
582 // IFDETn low when they are inserted into the HSBP.This register, in
583 // combination with the PRESENCE register, are used by the BMC to detect
584 // the presence of NVMe drives.
585
586 constexpr uint8_t addr = 9;
587
588 int ret = i2c_smbus_read_byte_data(file, addr);
589 if (ret < 0)
590 {
591 std::cerr << "Error " << __FUNCTION__ << "\n";
592 return false;
593 }
594 // ifdet is inverted
595 val = static_cast<uint8_t>(~ret);
596 return true;
597 }
598
599 bool getFailed(uint8_t& val)
600 {
601 constexpr uint8_t addr = 0xC;
602 int ret = i2c_smbus_read_byte_data(file, addr);
603 if (ret < 0)
604 {
605 std::cerr << "Error " << __FUNCTION__ << "\n";
606 return false;
607 }
608 val = static_cast<uint8_t>(ret);
609 return true;
610 }
611
612 bool getRebuild(uint8_t& val)
613 {
614 constexpr uint8_t addr = 0xD;
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 val = static_cast<uint8_t>(ret);
622 return true;
623 }
624
James Feist09dd2312019-10-09 09:29:03 -0700625 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700626 {
627 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700628 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700629 if (file >= 0)
630 {
631 close(file);
632 }
633 }
634
635 size_t bus;
636 size_t address;
James Feist45772222019-09-27 10:38:08 -0700637 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700638 std::string name;
James Feist45772222019-09-27 10:38:08 -0700639 std::shared_ptr<boost::asio::steady_timer> timer;
640 bool present = false;
641 uint8_t typeId = 0;
642 uint8_t bootVer = 0;
643 uint8_t fpgaVer = 0;
644 uint8_t securityRev = 0;
645 uint8_t funSupported = 0;
646 uint8_t presence = 0;
647 uint8_t ifdet = 0;
648 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700649 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700650
651 int file = -1;
652
Feist, Jamesc95cf672019-08-29 16:10:35 -0700653 std::string type;
654
655 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700656 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
James Feistd0d36f12019-11-21 10:19:44 -0800657 std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
658 std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
James Feist45772222019-09-27 10:38:08 -0700659
James Feist42b49c12019-10-29 15:18:43 -0700660 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700661 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -0700662 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700663};
664
665std::unordered_map<std::string, Backplane> backplanes;
James Feist42b49c12019-10-29 15:18:43 -0700666std::list<Drive> ownerlessDrives; // drives without a backplane
Feist, Jamesc95cf672019-08-29 16:10:35 -0700667
James Feist8675a912019-10-16 14:36:58 -0700668static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -0700669{
James Feist8675a912019-10-16 14:36:58 -0700670 size_t count = 0;
671 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -0700672 {
James Feist8675a912019-10-16 14:36:58 -0700673 count += backplane.drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700674 }
James Feist8675a912019-10-16 14:36:58 -0700675 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700676}
677
James Feist8675a912019-10-16 14:36:58 -0700678void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -0700679{
James Feist8675a912019-10-16 14:36:58 -0700680 static constexpr const char* nvmeType =
681 "xyz.openbmc_project.Inventory.Item.NVMe";
James Feist0b236ab2019-10-02 09:09:16 -0700682
683 conn->async_method_call(
684 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
685 if (ec)
686 {
687 std::cerr << "Error contacting mapper " << ec.message() << "\n";
688 return;
689 }
James Feist8675a912019-10-16 14:36:58 -0700690
691 // drives may get an owner during this, or we might disover more
692 // drives
693 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -0700694 for (const auto& [path, objDict] : subtree)
695 {
696 if (objDict.empty())
697 {
698 continue;
699 }
700
701 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700702 // we export this interface too
703 if (owner == busName)
704 {
705 continue;
706 }
James Feist8675a912019-10-16 14:36:58 -0700707 if (std::find(objDict.begin()->second.begin(),
708 objDict.begin()->second.end(),
709 assetTag) == objDict.begin()->second.end())
710 {
711 // no asset tag to associate to
712 continue;
713 }
714
James Feist0b236ab2019-10-02 09:09:16 -0700715 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700716 [path](const boost::system::error_code ec2,
717 const boost::container::flat_map<
718 std::string,
719 std::variant<uint64_t, std::string>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700720 if (ec2)
721 {
722 std::cerr << "Error Getting Config "
723 << ec2.message() << " " << __FUNCTION__
724 << "\n";
725 return;
726 }
727 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -0700728
James Feist8675a912019-10-16 14:36:58 -0700729 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -0700730 {
731 std::cerr << "Illegal interface at " << path
732 << "\n";
733 return;
734 }
735
James Feist8675a912019-10-16 14:36:58 -0700736 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -0700737 size_t muxBus = static_cast<size_t>(
738 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -0700739 std::filesystem::path muxPath =
740 "/sys/bus/i2c/devices/i2c-" +
741 std::to_string(muxBus) + "/mux_device";
742 if (!std::filesystem::is_symlink(muxPath))
743 {
744 std::cerr << path << " mux does not exist\n";
745 return;
746 }
747
James Feist8675a912019-10-16 14:36:58 -0700748 // we should be getting something of the form 7-0052
749 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -0700750 std::string fname =
751 std::filesystem::read_symlink(muxPath).filename();
752 auto findDash = fname.find('-');
753
754 if (findDash == std::string::npos ||
755 findDash + 1 >= fname.size())
756 {
757 std::cerr << path << " mux path invalid\n";
758 return;
759 }
760
761 std::string busStr = fname.substr(0, findDash);
762 std::string muxStr = fname.substr(findDash + 1);
763
764 size_t bus = static_cast<size_t>(std::stoi(busStr));
765 size_t addr =
766 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -0700767 size_t muxIndex = 0;
768
769 // find the channel of the mux the drive is on
770 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
771 std::to_string(muxBus) +
772 "/name");
773 if (!nameFile)
774 {
775 std::cerr << "Unable to open name file of bus "
776 << muxBus << "\n";
777 return;
778 }
779
780 std::string nameStr;
781 std::getline(nameFile, nameStr);
782
783 // file is of the form "i2c-4-mux (chan_id 1)", get chan
784 // assume single digit chan
785 const std::string prefix = "chan_id ";
786 size_t findId = nameStr.find(prefix);
787 if (findId == std::string::npos ||
788 findId + 1 >= nameStr.size())
789 {
790 std::cerr << "Illegal name file on bus " << muxBus
791 << "\n";
792 }
793
794 std::string indexStr =
795 nameStr.substr(findId + prefix.size(), 1);
796
797 size_t driveIndex = std::stoi(indexStr);
798
James Feist0b236ab2019-10-02 09:09:16 -0700799 Backplane* parent = nullptr;
800 for (auto& [name, backplane] : backplanes)
801 {
James Feist8675a912019-10-16 14:36:58 -0700802 muxIndex = 0;
James Feist0b236ab2019-10-02 09:09:16 -0700803 for (const Mux& mux : *(backplane.muxes))
804 {
805 if (bus == mux.bus && addr == mux.address)
806 {
807 parent = &backplane;
808 break;
809 }
James Feist8675a912019-10-16 14:36:58 -0700810 muxIndex += mux.channels;
James Feist0b236ab2019-10-02 09:09:16 -0700811 }
812 }
James Feist8675a912019-10-16 14:36:58 -0700813 boost::container::flat_map<std::string, std::string>
814 assetInventory;
815 const std::array<const char*, 4> assetKeys = {
816 "PartNumber", "SerialNumber", "Manufacturer",
817 "Model"};
818 for (const auto& [key, value] : values)
819 {
820 if (std::find(assetKeys.begin(), assetKeys.end(),
821 key) == assetKeys.end())
822 {
823 continue;
824 }
825 assetInventory[key] = std::get<std::string>(value);
826 }
827
828 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -0700829 if (parent == nullptr)
830 {
James Feist8675a912019-10-16 14:36:58 -0700831 auto& drive = ownerlessDrives.emplace_back(
832 getDriveCount() + 1, true, true, true, false);
833 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700834 return;
835 }
James Feist8675a912019-10-16 14:36:58 -0700836
837 driveIndex += muxIndex;
838
James Feist0b236ab2019-10-02 09:09:16 -0700839 if (parent->drives.size() <= driveIndex)
840 {
James Feist0b236ab2019-10-02 09:09:16 -0700841 std::cerr << "Illegal drive index at " << path
842 << " " << driveIndex << "\n";
843 return;
844 }
James Feist42b49c12019-10-29 15:18:43 -0700845 auto it = parent->drives.begin();
846 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -0700847
James Feist42b49c12019-10-29 15:18:43 -0700848 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700849 },
850 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -0700851 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -0700852 }
853 },
854 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
James Feist8675a912019-10-16 14:36:58 -0700855 0, std::array<const char*, 1>{nvmeType});
James Feist0b236ab2019-10-02 09:09:16 -0700856}
857
James Feist8675a912019-10-16 14:36:58 -0700858void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -0700859 std::string& rootPath)
860{
861 const static std::array<const std::string, 4> muxTypes = {
862 "xyz.openbmc_project.Configuration.PCA9543Mux",
863 "xyz.openbmc_project.Configuration.PCA9544Mux",
864 "xyz.openbmc_project.Configuration.PCA9545Mux",
865 "xyz.openbmc_project.Configuration.PCA9546Mux"};
866 conn->async_method_call(
867 [muxes](const boost::system::error_code ec,
868 const GetSubTreeType& subtree) {
869 if (ec)
870 {
871 std::cerr << "Error contacting mapper " << ec.message() << "\n";
872 return;
873 }
874 std::shared_ptr<std::function<void()>> callback =
875 std::make_shared<std::function<void()>>(
James Feist8675a912019-10-16 14:36:58 -0700876 []() { updateAssets(); });
877 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -0700878 for (const auto& [path, objDict] : subtree)
879 {
880 if (objDict.empty() || objDict.begin()->second.empty())
881 {
882 continue;
883 }
884
885 const std::string& owner = objDict.begin()->first;
886 const std::vector<std::string>& interfaces =
887 objDict.begin()->second;
888
889 const std::string* interface = nullptr;
890 for (const std::string& iface : interfaces)
891 {
892 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
893 muxTypes.end())
894 {
895 interface = &iface;
896 break;
897 }
898 }
899 if (interface == nullptr)
900 {
901 std::cerr << "Cannot get mux type\n";
902 continue;
903 }
904
905 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700906 [path, muxes, callback, index](
James Feist0b236ab2019-10-02 09:09:16 -0700907 const boost::system::error_code ec2,
908 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -0700909 std::string,
910 std::variant<uint64_t, std::vector<std::string>>>&
911 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700912 if (ec2)
913 {
914 std::cerr << "Error Getting Config "
915 << ec2.message() << " " << __FUNCTION__
916 << "\n";
917 return;
918 }
919 auto findBus = values.find("Bus");
920 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -0700921 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -0700922 if (findBus == values.end() ||
923 findAddress == values.end())
924 {
925 std::cerr << "Illegal configuration at " << path
926 << "\n";
927 return;
928 }
929 size_t bus = static_cast<size_t>(
930 std::get<uint64_t>(findBus->second));
931 size_t address = static_cast<size_t>(
932 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -0700933 std::vector<std::string> channels =
934 std::get<std::vector<std::string>>(
935 findChannelNames->second);
936 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -0700937 if (callback.use_count() == 1)
938 {
939 (*callback)();
940 }
941 },
942 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
943 *interface);
James Feist8675a912019-10-16 14:36:58 -0700944 index++;
James Feist0b236ab2019-10-02 09:09:16 -0700945 }
946 },
947 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
948 rootPath, 1, muxTypes);
949}
950
Feist, Jamesc95cf672019-08-29 16:10:35 -0700951void populate()
952{
953 conn->async_method_call(
954 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
955 if (ec)
956 {
957 std::cerr << "Error contacting mapper " << ec.message() << "\n";
958 return;
959 }
960 for (const auto& [path, objDict] : subtree)
961 {
962 if (objDict.empty())
963 {
964 continue;
965 }
966
967 const std::string& owner = objDict.begin()->first;
968 conn->async_method_call(
James Feistd0d36f12019-11-21 10:19:44 -0800969 [path, owner](const boost::system::error_code ec2,
970 const boost::container::flat_map<
971 std::string, BasicVariantType>& resp) {
Feist, Jamesc95cf672019-08-29 16:10:35 -0700972 if (ec2)
973 {
974 std::cerr << "Error Getting Config "
975 << ec2.message() << "\n";
976 return;
977 }
978 backplanes.clear();
979 std::optional<size_t> bus;
980 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700981 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700982 std::optional<std::string> name;
983 for (const auto& [key, value] : resp)
984 {
985 if (key == "Bus")
986 {
987 bus = std::get<uint64_t>(value);
988 }
989 else if (key == "Address")
990 {
991 address = std::get<uint64_t>(value);
992 }
James Feist45772222019-09-27 10:38:08 -0700993 else if (key == "Index")
994 {
995 backplaneIndex = std::get<uint64_t>(value);
996 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700997 else if (key == "Name")
998 {
999 name = std::get<std::string>(value);
1000 }
1001 }
James Feist45772222019-09-27 10:38:08 -07001002 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -07001003 {
1004 std::cerr << "Illegal configuration at " << path
1005 << "\n";
1006 return;
1007 }
James Feist0b236ab2019-10-02 09:09:16 -07001008 std::string parentPath =
1009 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -07001010 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -07001011 *name,
1012 Backplane(*bus, *address, *backplaneIndex, *name));
James Feistd0d36f12019-11-21 10:19:44 -08001013 backplane->second.run(parentPath, owner);
James Feist0b236ab2019-10-02 09:09:16 -07001014 populateMuxes(backplane->second.muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001015 },
1016 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1017 configType);
1018 }
1019 },
1020 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1021 0, std::array<const char*, 1>{configType});
1022}
1023
1024int main()
1025{
1026 boost::asio::steady_timer callbackTimer(io);
1027
James Feistdb2e0e72019-10-07 16:34:06 -07001028 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001029
1030 sdbusplus::bus::match::match match(
1031 *conn,
1032 "type='signal',member='PropertiesChanged',arg0='" +
1033 std::string(configType) + "'",
1034 [&callbackTimer](sdbusplus::message::message&) {
1035 callbackTimer.expires_after(std::chrono::seconds(2));
1036 callbackTimer.async_wait([](const boost::system::error_code ec) {
1037 if (ec == boost::asio::error::operation_aborted)
1038 {
1039 // timer was restarted
1040 return;
1041 }
1042 else if (ec)
1043 {
1044 std::cerr << "Timer error" << ec.message() << "\n";
1045 return;
1046 }
1047 populate();
1048 });
1049 });
1050
James Feist0b236ab2019-10-02 09:09:16 -07001051 sdbusplus::bus::match::match drive(
1052 *conn,
1053 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
James Feist8675a912019-10-16 14:36:58 -07001054 "Inventory.Item.NVMe'",
James Feistdb2e0e72019-10-07 16:34:06 -07001055 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -07001056 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -07001057 if (message.get_sender() == conn->get_unique_name())
1058 {
1059 return;
1060 }
James Feist0b236ab2019-10-02 09:09:16 -07001061 callbackTimer.async_wait([](const boost::system::error_code ec) {
1062 if (ec == boost::asio::error::operation_aborted)
1063 {
1064 // timer was restarted
1065 return;
1066 }
1067 else if (ec)
1068 {
1069 std::cerr << "Timer error" << ec.message() << "\n";
1070 return;
1071 }
1072 populate();
1073 });
1074 });
1075
James Feist42b49c12019-10-29 15:18:43 -07001076 auto iface =
1077 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
1078 "xyz.openbmc_project.inventory.item.storage");
1079
Feist, Jamesc95cf672019-08-29 16:10:35 -07001080 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -07001081 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001082 io.run();
1083}