blob: d65d80ccc31c6d7ac01c71ff68766a9240e11b98 [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),
151 isPresent(present)
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 Feistdb2e0e72019-10-07 16:34:06 -0700229 }
James Feist8675a912019-10-16 14:36:58 -0700230 assetIface->initialize();
James Feistdb2e0e72019-10-07 16:34:06 -0700231 }
232
James Feist42b49c12019-10-29 15:18:43 -0700233 void markFailed(void)
234 {
235 // todo: maybe look this up via mapper
236 constexpr const char* globalInventoryPath =
237 "/xyz/openbmc_project/CallbackManager";
238
239 if (!isPresent)
240 {
241 return;
242 }
243
244 operationalIface->set_property("Functional", false);
245 std::vector<Association> warning = {
246 {"", "warning", globalInventoryPath}};
247 associations->set_property("Associations", warning);
248 }
249
250 void clearFailed(void)
251 {
252 operationalIface->set_property("Functional", true);
253 associations->set_property("Associations", std::vector<Association>{});
254 }
255
James Feist45772222019-09-27 10:38:08 -0700256 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
257 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700258 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist8675a912019-10-16 14:36:58 -0700259 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700260 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
James Feist42b49c12019-10-29 15:18:43 -0700261 std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
James Feistdb2e0e72019-10-07 16:34:06 -0700262
James Feist45772222019-09-27 10:38:08 -0700263 bool isNvme;
James Feist42b49c12019-10-29 15:18:43 -0700264 bool isPresent;
James Feist45772222019-09-27 10:38:08 -0700265};
266
Feist, Jamesc95cf672019-08-29 16:10:35 -0700267struct Backplane
268{
269
James Feist45772222019-09-27 10:38:08 -0700270 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
271 const std::string& nameIn) :
272 bus(busIn),
273 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feist0b236ab2019-10-02 09:09:16 -0700274 timer(std::make_shared<boost::asio::steady_timer>(io)),
James Feist8675a912019-10-16 14:36:58 -0700275 muxes(std::make_shared<boost::container::flat_set<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700276 {
277 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700278 void run()
279 {
James Feist09dd2312019-10-09 09:29:03 -0700280 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
281 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700282 if (file < 0)
283 {
284 std::cerr << "unable to open bus " << bus << "\n";
285 return;
286 }
287
288 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
289 {
290 std::cerr << "unable to set address to " << address << "\n";
291 return;
292 }
293
James Feist45772222019-09-27 10:38:08 -0700294 if (!getPresent())
295 {
296 std::cerr << "Cannot detect CPLD\n";
297 return;
298 }
299
300 getBootVer(bootVer);
301 getFPGAVer(fpgaVer);
302 getSecurityRev(securityRev);
303 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700304 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700305 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700306 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700307 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700308 hsbpItemIface->register_property("PrettyName", name);
309 hsbpItemIface->initialize();
310
James Feist45772222019-09-27 10:38:08 -0700311 versionIface =
312 objServer.add_interface(hsbpItemIface->get_object_path(),
313 "xyz.openbmc_project.Software.Version");
314 versionIface->register_property("Version", zeroPad(bootVer) + "." +
315 zeroPad(fpgaVer) + "." +
316 zeroPad(securityRev));
317 versionIface->register_property(
318 "Purpose",
319 std::string(
320 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
321 versionIface->initialize();
322 getPresence(presence);
323 getIFDET(ifdet);
324
325 createDrives();
326
327 runTimer();
328 }
329
330 void runTimer()
331 {
332 timer->expires_after(std::chrono::seconds(scanRateSeconds));
333 timer->async_wait([this](boost::system::error_code ec) {
334 if (ec == boost::asio::error::operation_aborted)
335 {
336 // we're being destroyed
337 return;
338 }
339 else if (ec)
340 {
341 std::cerr << "timer error " << ec.message() << "\n";
342 return;
343 }
James Feist9f6565d2019-10-09 13:15:13 -0700344
345 if (!isPowerOn())
346 {
347 // can't access hsbp when power is off
348 runTimer();
349 return;
350 }
James Feist45772222019-09-27 10:38:08 -0700351 uint8_t curPresence = 0;
352 uint8_t curIFDET = 0;
353 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700354 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700355
356 getPresence(curPresence);
357 getIFDET(curIFDET);
358 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700359 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700360
361 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700362 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700363 {
364 presence = curPresence;
365 ifdet = curIFDET;
366 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700367 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700368 updateDrives();
369 }
370 runTimer();
371 });
372 }
373
374 void createDrives()
375 {
376 uint8_t nvme = ifdet ^ presence;
377 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700378 {
James Feist45772222019-09-27 10:38:08 -0700379 bool isNvme = nvme & (1 << ii);
380 bool isPresent = isNvme || (presence & (1 << ii));
381 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700382 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700383
384 // +1 to convert from 0 based to 1 based
385 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700386 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
387 isNvme, isRebuilding);
388 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
389 drive.itemIface->get_object_path(), ii, file));
390 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700391 }
392 }
393
James Feist45772222019-09-27 10:38:08 -0700394 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700395 {
James Feist45772222019-09-27 10:38:08 -0700396
397 uint8_t nvme = ifdet ^ presence;
James Feist42b49c12019-10-29 15:18:43 -0700398 size_t ii = 0;
399
400 for (auto it = drives.begin(); it != drives.end(); it++, ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700401 {
James Feist45772222019-09-27 10:38:08 -0700402 bool isNvme = nvme & (1 << ii);
403 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700404 bool isFailed = !isPresent || (failed & (1 << ii));
405 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700406
James Feist42b49c12019-10-29 15:18:43 -0700407 it->isNvme = isNvme;
408 it->itemIface->set_property("Present", isPresent);
409 it->isPresent = isPresent;
410 it->rebuildingIface->set_property("Rebuilding", isRebuilding);
411 if (isFailed || isRebuilding)
412 {
413 it->markFailed();
414 }
415 else
416 {
417 it->clearFailed();
418 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700419 }
James Feist45772222019-09-27 10:38:08 -0700420 }
421
422 bool getPresent()
423 {
424 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700425 return present;
426 }
James Feist45772222019-09-27 10:38:08 -0700427
428 bool getTypeID(uint8_t& val)
429 {
430 constexpr uint8_t addr = 2;
431 int ret = i2c_smbus_read_byte_data(file, addr);
432 if (ret < 0)
433 {
434 std::cerr << "Error " << __FUNCTION__ << "\n";
435 return false;
436 }
437 val = static_cast<uint8_t>(ret);
438 return true;
439 }
440
441 bool getBootVer(uint8_t& val)
442 {
443 constexpr uint8_t addr = 3;
444 int ret = i2c_smbus_read_byte_data(file, addr);
445 if (ret < 0)
446 {
447 std::cerr << "Error " << __FUNCTION__ << "\n";
448 return false;
449 }
450 val = static_cast<uint8_t>(ret);
451 return true;
452 }
453
454 bool getFPGAVer(uint8_t& val)
455 {
456 constexpr uint8_t addr = 4;
457 int ret = i2c_smbus_read_byte_data(file, addr);
458 if (ret < 0)
459 {
460 std::cerr << "Error " << __FUNCTION__ << "\n";
461 return false;
462 }
463 val = static_cast<uint8_t>(ret);
464 return true;
465 }
466
467 bool getSecurityRev(uint8_t& val)
468 {
469 constexpr uint8_t addr = 5;
470 int ret = i2c_smbus_read_byte_data(file, addr);
471 if (ret < 0)
472 {
473 std::cerr << "Error " << __FUNCTION__ << "\n";
474 return false;
475 }
476 val = static_cast<uint8_t>(ret);
477 return true;
478 }
479
480 bool getPresence(uint8_t& val)
481 {
482 // NVMe drives do not assert PRSNTn, and as such do not get reported as
483 // PRESENT in this register
484
485 constexpr uint8_t addr = 8;
486
487 int ret = i2c_smbus_read_byte_data(file, addr);
488 if (ret < 0)
489 {
490 std::cerr << "Error " << __FUNCTION__ << "\n";
491 return false;
492 }
493 // presence is inverted
494 val = static_cast<uint8_t>(~ret);
495 return true;
496 }
497
498 bool getIFDET(uint8_t& val)
499 {
500 // This register is a bitmap of parallel GPIO pins connected to the
501 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
502 // IFDETn low when they are inserted into the HSBP.This register, in
503 // combination with the PRESENCE register, are used by the BMC to detect
504 // the presence of NVMe drives.
505
506 constexpr uint8_t addr = 9;
507
508 int ret = i2c_smbus_read_byte_data(file, addr);
509 if (ret < 0)
510 {
511 std::cerr << "Error " << __FUNCTION__ << "\n";
512 return false;
513 }
514 // ifdet is inverted
515 val = static_cast<uint8_t>(~ret);
516 return true;
517 }
518
519 bool getFailed(uint8_t& val)
520 {
521 constexpr uint8_t addr = 0xC;
522 int ret = i2c_smbus_read_byte_data(file, addr);
523 if (ret < 0)
524 {
525 std::cerr << "Error " << __FUNCTION__ << "\n";
526 return false;
527 }
528 val = static_cast<uint8_t>(ret);
529 return true;
530 }
531
532 bool getRebuild(uint8_t& val)
533 {
534 constexpr uint8_t addr = 0xD;
535 int ret = i2c_smbus_read_byte_data(file, addr);
536 if (ret < 0)
537 {
538 std::cerr << "Error " << __FUNCTION__ << "\n";
539 return false;
540 }
541 val = static_cast<uint8_t>(ret);
542 return true;
543 }
544
James Feist09dd2312019-10-09 09:29:03 -0700545 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700546 {
547 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700548 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700549 if (file >= 0)
550 {
551 close(file);
552 }
553 }
554
555 size_t bus;
556 size_t address;
James Feist45772222019-09-27 10:38:08 -0700557 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700558 std::string name;
James Feist45772222019-09-27 10:38:08 -0700559 std::shared_ptr<boost::asio::steady_timer> timer;
560 bool present = false;
561 uint8_t typeId = 0;
562 uint8_t bootVer = 0;
563 uint8_t fpgaVer = 0;
564 uint8_t securityRev = 0;
565 uint8_t funSupported = 0;
566 uint8_t presence = 0;
567 uint8_t ifdet = 0;
568 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700569 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700570
571 int file = -1;
572
Feist, Jamesc95cf672019-08-29 16:10:35 -0700573 std::string type;
574
575 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700576 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
577
James Feist42b49c12019-10-29 15:18:43 -0700578 std::list<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700579 std::vector<std::shared_ptr<Led>> leds;
James Feist8675a912019-10-16 14:36:58 -0700580 std::shared_ptr<boost::container::flat_set<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700581};
582
583std::unordered_map<std::string, Backplane> backplanes;
James Feist42b49c12019-10-29 15:18:43 -0700584std::list<Drive> ownerlessDrives; // drives without a backplane
Feist, Jamesc95cf672019-08-29 16:10:35 -0700585
James Feist8675a912019-10-16 14:36:58 -0700586static size_t getDriveCount()
James Feistdb2e0e72019-10-07 16:34:06 -0700587{
James Feist8675a912019-10-16 14:36:58 -0700588 size_t count = 0;
589 for (const auto& [key, backplane] : backplanes)
James Feistdb2e0e72019-10-07 16:34:06 -0700590 {
James Feist8675a912019-10-16 14:36:58 -0700591 count += backplane.drives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700592 }
James Feist8675a912019-10-16 14:36:58 -0700593 return count + ownerlessDrives.size();
James Feistdb2e0e72019-10-07 16:34:06 -0700594}
595
James Feist8675a912019-10-16 14:36:58 -0700596void updateAssets()
James Feist0b236ab2019-10-02 09:09:16 -0700597{
James Feist8675a912019-10-16 14:36:58 -0700598 static constexpr const char* nvmeType =
599 "xyz.openbmc_project.Inventory.Item.NVMe";
600 static const std::string assetTag =
601 "xyz.openbmc_project.Inventory.Decorator.Asset";
James Feist0b236ab2019-10-02 09:09:16 -0700602
603 conn->async_method_call(
604 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
605 if (ec)
606 {
607 std::cerr << "Error contacting mapper " << ec.message() << "\n";
608 return;
609 }
James Feist8675a912019-10-16 14:36:58 -0700610
611 // drives may get an owner during this, or we might disover more
612 // drives
613 ownerlessDrives.clear();
James Feist0b236ab2019-10-02 09:09:16 -0700614 for (const auto& [path, objDict] : subtree)
615 {
616 if (objDict.empty())
617 {
618 continue;
619 }
620
621 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700622 // we export this interface too
623 if (owner == busName)
624 {
625 continue;
626 }
James Feist8675a912019-10-16 14:36:58 -0700627 if (std::find(objDict.begin()->second.begin(),
628 objDict.begin()->second.end(),
629 assetTag) == objDict.begin()->second.end())
630 {
631 // no asset tag to associate to
632 continue;
633 }
634
James Feist0b236ab2019-10-02 09:09:16 -0700635 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700636 [path](const boost::system::error_code ec2,
637 const boost::container::flat_map<
638 std::string,
639 std::variant<uint64_t, std::string>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700640 if (ec2)
641 {
642 std::cerr << "Error Getting Config "
643 << ec2.message() << " " << __FUNCTION__
644 << "\n";
645 return;
646 }
647 auto findBus = values.find("Bus");
James Feist0b236ab2019-10-02 09:09:16 -0700648
James Feist8675a912019-10-16 14:36:58 -0700649 if (findBus == values.end())
James Feist0b236ab2019-10-02 09:09:16 -0700650 {
651 std::cerr << "Illegal interface at " << path
652 << "\n";
653 return;
654 }
655
James Feist8675a912019-10-16 14:36:58 -0700656 // find the mux bus and addr
James Feist0b236ab2019-10-02 09:09:16 -0700657 size_t muxBus = static_cast<size_t>(
658 std::get<uint64_t>(findBus->second));
James Feist0b236ab2019-10-02 09:09:16 -0700659 std::filesystem::path muxPath =
660 "/sys/bus/i2c/devices/i2c-" +
661 std::to_string(muxBus) + "/mux_device";
662 if (!std::filesystem::is_symlink(muxPath))
663 {
664 std::cerr << path << " mux does not exist\n";
665 return;
666 }
667
James Feist8675a912019-10-16 14:36:58 -0700668 // we should be getting something of the form 7-0052
669 // for bus 7 addr 52
James Feist0b236ab2019-10-02 09:09:16 -0700670 std::string fname =
671 std::filesystem::read_symlink(muxPath).filename();
672 auto findDash = fname.find('-');
673
674 if (findDash == std::string::npos ||
675 findDash + 1 >= fname.size())
676 {
677 std::cerr << path << " mux path invalid\n";
678 return;
679 }
680
681 std::string busStr = fname.substr(0, findDash);
682 std::string muxStr = fname.substr(findDash + 1);
683
684 size_t bus = static_cast<size_t>(std::stoi(busStr));
685 size_t addr =
686 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
James Feist8675a912019-10-16 14:36:58 -0700687 size_t muxIndex = 0;
688
689 // find the channel of the mux the drive is on
690 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
691 std::to_string(muxBus) +
692 "/name");
693 if (!nameFile)
694 {
695 std::cerr << "Unable to open name file of bus "
696 << muxBus << "\n";
697 return;
698 }
699
700 std::string nameStr;
701 std::getline(nameFile, nameStr);
702
703 // file is of the form "i2c-4-mux (chan_id 1)", get chan
704 // assume single digit chan
705 const std::string prefix = "chan_id ";
706 size_t findId = nameStr.find(prefix);
707 if (findId == std::string::npos ||
708 findId + 1 >= nameStr.size())
709 {
710 std::cerr << "Illegal name file on bus " << muxBus
711 << "\n";
712 }
713
714 std::string indexStr =
715 nameStr.substr(findId + prefix.size(), 1);
716
717 size_t driveIndex = std::stoi(indexStr);
718
James Feist0b236ab2019-10-02 09:09:16 -0700719 Backplane* parent = nullptr;
720 for (auto& [name, backplane] : backplanes)
721 {
James Feist8675a912019-10-16 14:36:58 -0700722 muxIndex = 0;
James Feist0b236ab2019-10-02 09:09:16 -0700723 for (const Mux& mux : *(backplane.muxes))
724 {
725 if (bus == mux.bus && addr == mux.address)
726 {
727 parent = &backplane;
728 break;
729 }
James Feist8675a912019-10-16 14:36:58 -0700730 muxIndex += mux.channels;
James Feist0b236ab2019-10-02 09:09:16 -0700731 }
732 }
James Feist8675a912019-10-16 14:36:58 -0700733 boost::container::flat_map<std::string, std::string>
734 assetInventory;
735 const std::array<const char*, 4> assetKeys = {
736 "PartNumber", "SerialNumber", "Manufacturer",
737 "Model"};
738 for (const auto& [key, value] : values)
739 {
740 if (std::find(assetKeys.begin(), assetKeys.end(),
741 key) == assetKeys.end())
742 {
743 continue;
744 }
745 assetInventory[key] = std::get<std::string>(value);
746 }
747
748 // assume its a M.2 or something without a hsbp
James Feist0b236ab2019-10-02 09:09:16 -0700749 if (parent == nullptr)
750 {
James Feist8675a912019-10-16 14:36:58 -0700751 auto& drive = ownerlessDrives.emplace_back(
752 getDriveCount() + 1, true, true, true, false);
753 drive.createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700754 return;
755 }
James Feist8675a912019-10-16 14:36:58 -0700756
757 driveIndex += muxIndex;
758
James Feist0b236ab2019-10-02 09:09:16 -0700759 if (parent->drives.size() <= driveIndex)
760 {
James Feist0b236ab2019-10-02 09:09:16 -0700761 std::cerr << "Illegal drive index at " << path
762 << " " << driveIndex << "\n";
763 return;
764 }
James Feist42b49c12019-10-29 15:18:43 -0700765 auto it = parent->drives.begin();
766 std::advance(it, driveIndex);
James Feist8675a912019-10-16 14:36:58 -0700767
James Feist42b49c12019-10-29 15:18:43 -0700768 it->createAsset(assetInventory);
James Feist0b236ab2019-10-02 09:09:16 -0700769 },
770 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
James Feist8675a912019-10-16 14:36:58 -0700771 "" /*all interface items*/);
James Feist0b236ab2019-10-02 09:09:16 -0700772 }
773 },
774 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
James Feist8675a912019-10-16 14:36:58 -0700775 0, std::array<const char*, 1>{nvmeType});
James Feist0b236ab2019-10-02 09:09:16 -0700776}
777
James Feist8675a912019-10-16 14:36:58 -0700778void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
James Feist0b236ab2019-10-02 09:09:16 -0700779 std::string& rootPath)
780{
781 const static std::array<const std::string, 4> muxTypes = {
782 "xyz.openbmc_project.Configuration.PCA9543Mux",
783 "xyz.openbmc_project.Configuration.PCA9544Mux",
784 "xyz.openbmc_project.Configuration.PCA9545Mux",
785 "xyz.openbmc_project.Configuration.PCA9546Mux"};
786 conn->async_method_call(
787 [muxes](const boost::system::error_code ec,
788 const GetSubTreeType& subtree) {
789 if (ec)
790 {
791 std::cerr << "Error contacting mapper " << ec.message() << "\n";
792 return;
793 }
794 std::shared_ptr<std::function<void()>> callback =
795 std::make_shared<std::function<void()>>(
James Feist8675a912019-10-16 14:36:58 -0700796 []() { updateAssets(); });
797 size_t index = 0; // as we use a flat map, these are sorted
James Feist0b236ab2019-10-02 09:09:16 -0700798 for (const auto& [path, objDict] : subtree)
799 {
800 if (objDict.empty() || objDict.begin()->second.empty())
801 {
802 continue;
803 }
804
805 const std::string& owner = objDict.begin()->first;
806 const std::vector<std::string>& interfaces =
807 objDict.begin()->second;
808
809 const std::string* interface = nullptr;
810 for (const std::string& iface : interfaces)
811 {
812 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
813 muxTypes.end())
814 {
815 interface = &iface;
816 break;
817 }
818 }
819 if (interface == nullptr)
820 {
821 std::cerr << "Cannot get mux type\n";
822 continue;
823 }
824
825 conn->async_method_call(
James Feist8675a912019-10-16 14:36:58 -0700826 [path, muxes, callback, index](
James Feist0b236ab2019-10-02 09:09:16 -0700827 const boost::system::error_code ec2,
828 const boost::container::flat_map<
James Feist8675a912019-10-16 14:36:58 -0700829 std::string,
830 std::variant<uint64_t, std::vector<std::string>>>&
831 values) {
James Feist0b236ab2019-10-02 09:09:16 -0700832 if (ec2)
833 {
834 std::cerr << "Error Getting Config "
835 << ec2.message() << " " << __FUNCTION__
836 << "\n";
837 return;
838 }
839 auto findBus = values.find("Bus");
840 auto findAddress = values.find("Address");
James Feist8675a912019-10-16 14:36:58 -0700841 auto findChannelNames = values.find("ChannelNames");
James Feist0b236ab2019-10-02 09:09:16 -0700842 if (findBus == values.end() ||
843 findAddress == values.end())
844 {
845 std::cerr << "Illegal configuration at " << path
846 << "\n";
847 return;
848 }
849 size_t bus = static_cast<size_t>(
850 std::get<uint64_t>(findBus->second));
851 size_t address = static_cast<size_t>(
852 std::get<uint64_t>(findAddress->second));
James Feist8675a912019-10-16 14:36:58 -0700853 std::vector<std::string> channels =
854 std::get<std::vector<std::string>>(
855 findChannelNames->second);
856 muxes->emplace(bus, address, channels.size(), index);
James Feist0b236ab2019-10-02 09:09:16 -0700857 if (callback.use_count() == 1)
858 {
859 (*callback)();
860 }
861 },
862 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
863 *interface);
James Feist8675a912019-10-16 14:36:58 -0700864 index++;
James Feist0b236ab2019-10-02 09:09:16 -0700865 }
866 },
867 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
868 rootPath, 1, muxTypes);
869}
870
Feist, Jamesc95cf672019-08-29 16:10:35 -0700871void populate()
872{
873 conn->async_method_call(
874 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
875 if (ec)
876 {
877 std::cerr << "Error contacting mapper " << ec.message() << "\n";
878 return;
879 }
880 for (const auto& [path, objDict] : subtree)
881 {
882 if (objDict.empty())
883 {
884 continue;
885 }
886
887 const std::string& owner = objDict.begin()->first;
888 conn->async_method_call(
889 [path](const boost::system::error_code ec2,
890 const boost::container::flat_map<
891 std::string, BasicVariantType>& resp) {
892 if (ec2)
893 {
894 std::cerr << "Error Getting Config "
895 << ec2.message() << "\n";
896 return;
897 }
898 backplanes.clear();
899 std::optional<size_t> bus;
900 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700901 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700902 std::optional<std::string> name;
903 for (const auto& [key, value] : resp)
904 {
905 if (key == "Bus")
906 {
907 bus = std::get<uint64_t>(value);
908 }
909 else if (key == "Address")
910 {
911 address = std::get<uint64_t>(value);
912 }
James Feist45772222019-09-27 10:38:08 -0700913 else if (key == "Index")
914 {
915 backplaneIndex = std::get<uint64_t>(value);
916 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700917 else if (key == "Name")
918 {
919 name = std::get<std::string>(value);
920 }
921 }
James Feist45772222019-09-27 10:38:08 -0700922 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700923 {
924 std::cerr << "Illegal configuration at " << path
925 << "\n";
926 return;
927 }
James Feist0b236ab2019-10-02 09:09:16 -0700928 std::string parentPath =
929 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700930 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -0700931 *name,
932 Backplane(*bus, *address, *backplaneIndex, *name));
Feist, Jamesc95cf672019-08-29 16:10:35 -0700933 backplane->second.run();
James Feist0b236ab2019-10-02 09:09:16 -0700934 populateMuxes(backplane->second.muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700935 },
936 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
937 configType);
938 }
939 },
940 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
941 0, std::array<const char*, 1>{configType});
942}
943
944int main()
945{
946 boost::asio::steady_timer callbackTimer(io);
947
James Feistdb2e0e72019-10-07 16:34:06 -0700948 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700949
950 sdbusplus::bus::match::match match(
951 *conn,
952 "type='signal',member='PropertiesChanged',arg0='" +
953 std::string(configType) + "'",
954 [&callbackTimer](sdbusplus::message::message&) {
955 callbackTimer.expires_after(std::chrono::seconds(2));
956 callbackTimer.async_wait([](const boost::system::error_code ec) {
957 if (ec == boost::asio::error::operation_aborted)
958 {
959 // timer was restarted
960 return;
961 }
962 else if (ec)
963 {
964 std::cerr << "Timer error" << ec.message() << "\n";
965 return;
966 }
967 populate();
968 });
969 });
970
James Feist0b236ab2019-10-02 09:09:16 -0700971 sdbusplus::bus::match::match drive(
972 *conn,
973 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
James Feist8675a912019-10-16 14:36:58 -0700974 "Inventory.Item.NVMe'",
James Feistdb2e0e72019-10-07 16:34:06 -0700975 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -0700976 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -0700977 if (message.get_sender() == conn->get_unique_name())
978 {
979 return;
980 }
James Feist0b236ab2019-10-02 09:09:16 -0700981 callbackTimer.async_wait([](const boost::system::error_code ec) {
982 if (ec == boost::asio::error::operation_aborted)
983 {
984 // timer was restarted
985 return;
986 }
987 else if (ec)
988 {
989 std::cerr << "Timer error" << ec.message() << "\n";
990 return;
991 }
992 populate();
993 });
994 });
995
James Feist42b49c12019-10-29 15:18:43 -0700996 auto iface =
997 objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
998 "xyz.openbmc_project.inventory.item.storage");
999
Feist, Jamesc95cf672019-08-29 16:10:35 -07001000 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -07001001 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -07001002 io.run();
1003}