blob: 9dd4dbd5ebaf4cfe78f7c8936fe8caf27157bea3 [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>
21#include <iostream>
22#include <sdbusplus/asio/connection.hpp>
23#include <sdbusplus/asio/object_server.hpp>
24#include <sdbusplus/bus/match.hpp>
25#include <string>
James Feist45772222019-09-27 10:38:08 -070026#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070027
28extern "C" {
29#include <i2c/smbus.h>
30#include <linux/i2c-dev.h>
31}
32
33constexpr const char* configType =
34 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
35
James Feist45772222019-09-27 10:38:08 -070036constexpr size_t scanRateSeconds = 5;
37constexpr size_t maxDrives = 8; // only 1 byte alloted
38
Feist, Jamesc95cf672019-08-29 16:10:35 -070039boost::asio::io_context io;
40auto conn = std::make_shared<sdbusplus::asio::connection>(io);
41sdbusplus::asio::object_server objServer(conn);
42
James Feist45772222019-09-27 10:38:08 -070043static std::string zeroPad(const uint8_t val)
44{
45 std::ostringstream version;
46 version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val);
47 return version.str();
48}
49
50struct Drive
51{
James Feist244f3232019-09-27 15:15:14 -070052 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme,
53 bool rebuilding) :
James Feist45772222019-09-27 10:38:08 -070054 isNvme(nvme)
55 {
56 constexpr const char* basePath =
57 "/xyz/openbmc_project/inventory/item/drive/Drive_";
58 itemIface = objServer.add_interface(
59 basePath + std::to_string(driveIndex), inventory::interface);
60 itemIface->register_property("Present", isPresent);
61 itemIface->register_property("PrettyName",
62 "Drive " + std::to_string(driveIndex));
63 itemIface->initialize();
64 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -070065 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -070066 "xyz.openbmc_project.State.Decorator.OperationalStatus");
67 operationalIface->register_property("Functional", isOperational);
68 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -070069 rebuildingIface = objServer.add_interface(
70 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
71 rebuildingIface->register_property("Rebuilding", rebuilding);
72 rebuildingIface->initialize();
James Feist45772222019-09-27 10:38:08 -070073 }
74 ~Drive()
75 {
76 objServer.remove_interface(itemIface);
77 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -070078 objServer.remove_interface(rebuildingIface);
James Feist45772222019-09-27 10:38:08 -070079 }
80
81 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
82 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -070083 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist45772222019-09-27 10:38:08 -070084 bool isNvme;
85};
86
Feist, Jamesc95cf672019-08-29 16:10:35 -070087struct Backplane
88{
89
James Feist45772222019-09-27 10:38:08 -070090 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
91 const std::string& nameIn) :
92 bus(busIn),
93 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
94 timer(std::make_shared<boost::asio::steady_timer>(io))
Feist, Jamesc95cf672019-08-29 16:10:35 -070095 {
96 }
Feist, Jamesc95cf672019-08-29 16:10:35 -070097 void run()
98 {
99 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), O_RDWR);
100 if (file < 0)
101 {
102 std::cerr << "unable to open bus " << bus << "\n";
103 return;
104 }
105
106 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
107 {
108 std::cerr << "unable to set address to " << address << "\n";
109 return;
110 }
111
James Feist45772222019-09-27 10:38:08 -0700112 if (!getPresent())
113 {
114 std::cerr << "Cannot detect CPLD\n";
115 return;
116 }
117
118 getBootVer(bootVer);
119 getFPGAVer(fpgaVer);
120 getSecurityRev(securityRev);
121 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700122 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700123 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700124 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700125 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700126 hsbpItemIface->register_property("PrettyName", name);
127 hsbpItemIface->initialize();
128
James Feist45772222019-09-27 10:38:08 -0700129 versionIface =
130 objServer.add_interface(hsbpItemIface->get_object_path(),
131 "xyz.openbmc_project.Software.Version");
132 versionIface->register_property("Version", zeroPad(bootVer) + "." +
133 zeroPad(fpgaVer) + "." +
134 zeroPad(securityRev));
135 versionIface->register_property(
136 "Purpose",
137 std::string(
138 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
139 versionIface->initialize();
140 getPresence(presence);
141 getIFDET(ifdet);
142
143 createDrives();
144
145 runTimer();
146 }
147
148 void runTimer()
149 {
150 timer->expires_after(std::chrono::seconds(scanRateSeconds));
151 timer->async_wait([this](boost::system::error_code ec) {
152 if (ec == boost::asio::error::operation_aborted)
153 {
154 // we're being destroyed
155 return;
156 }
157 else if (ec)
158 {
159 std::cerr << "timer error " << ec.message() << "\n";
160 return;
161 }
162 uint8_t curPresence = 0;
163 uint8_t curIFDET = 0;
164 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700165 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700166
167 getPresence(curPresence);
168 getIFDET(curIFDET);
169 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700170 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700171
172 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700173 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700174 {
175 presence = curPresence;
176 ifdet = curIFDET;
177 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700178 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700179 updateDrives();
180 }
181 runTimer();
182 });
183 }
184
185 void createDrives()
186 {
187 uint8_t nvme = ifdet ^ presence;
188 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700189 {
James Feist45772222019-09-27 10:38:08 -0700190 bool isNvme = nvme & (1 << ii);
191 bool isPresent = isNvme || (presence & (1 << ii));
192 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700193 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700194
195 // +1 to convert from 0 based to 1 based
196 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist244f3232019-09-27 15:15:14 -0700197 drives.emplace_back(driveIndex, isPresent, !isFailed, isNvme,
198 isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700199 }
200 }
201
James Feist45772222019-09-27 10:38:08 -0700202 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700203 {
James Feist45772222019-09-27 10:38:08 -0700204
205 uint8_t nvme = ifdet ^ presence;
206 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700207 {
James Feist45772222019-09-27 10:38:08 -0700208 bool isNvme = nvme & (1 << ii);
209 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700210 bool isFailed = !isPresent || (failed & (1 << ii));
211 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700212
213 Drive& drive = drives[ii];
214 drive.isNvme = isNvme;
215 drive.itemIface->set_property("Present", isPresent);
216 drive.operationalIface->set_property("Functional", !isFailed);
James Feist244f3232019-09-27 15:15:14 -0700217 drive.rebuildingIface->set_property("Rebuilding", isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700218 }
James Feist45772222019-09-27 10:38:08 -0700219 }
220
221 bool getPresent()
222 {
223 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700224 return present;
225 }
James Feist45772222019-09-27 10:38:08 -0700226
227 bool getTypeID(uint8_t& val)
228 {
229 constexpr uint8_t addr = 2;
230 int ret = i2c_smbus_read_byte_data(file, addr);
231 if (ret < 0)
232 {
233 std::cerr << "Error " << __FUNCTION__ << "\n";
234 return false;
235 }
236 val = static_cast<uint8_t>(ret);
237 return true;
238 }
239
240 bool getBootVer(uint8_t& val)
241 {
242 constexpr uint8_t addr = 3;
243 int ret = i2c_smbus_read_byte_data(file, addr);
244 if (ret < 0)
245 {
246 std::cerr << "Error " << __FUNCTION__ << "\n";
247 return false;
248 }
249 val = static_cast<uint8_t>(ret);
250 return true;
251 }
252
253 bool getFPGAVer(uint8_t& val)
254 {
255 constexpr uint8_t addr = 4;
256 int ret = i2c_smbus_read_byte_data(file, addr);
257 if (ret < 0)
258 {
259 std::cerr << "Error " << __FUNCTION__ << "\n";
260 return false;
261 }
262 val = static_cast<uint8_t>(ret);
263 return true;
264 }
265
266 bool getSecurityRev(uint8_t& val)
267 {
268 constexpr uint8_t addr = 5;
269 int ret = i2c_smbus_read_byte_data(file, addr);
270 if (ret < 0)
271 {
272 std::cerr << "Error " << __FUNCTION__ << "\n";
273 return false;
274 }
275 val = static_cast<uint8_t>(ret);
276 return true;
277 }
278
279 bool getPresence(uint8_t& val)
280 {
281 // NVMe drives do not assert PRSNTn, and as such do not get reported as
282 // PRESENT in this register
283
284 constexpr uint8_t addr = 8;
285
286 int ret = i2c_smbus_read_byte_data(file, addr);
287 if (ret < 0)
288 {
289 std::cerr << "Error " << __FUNCTION__ << "\n";
290 return false;
291 }
292 // presence is inverted
293 val = static_cast<uint8_t>(~ret);
294 return true;
295 }
296
297 bool getIFDET(uint8_t& val)
298 {
299 // This register is a bitmap of parallel GPIO pins connected to the
300 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
301 // IFDETn low when they are inserted into the HSBP.This register, in
302 // combination with the PRESENCE register, are used by the BMC to detect
303 // the presence of NVMe drives.
304
305 constexpr uint8_t addr = 9;
306
307 int ret = i2c_smbus_read_byte_data(file, addr);
308 if (ret < 0)
309 {
310 std::cerr << "Error " << __FUNCTION__ << "\n";
311 return false;
312 }
313 // ifdet is inverted
314 val = static_cast<uint8_t>(~ret);
315 return true;
316 }
317
318 bool getFailed(uint8_t& val)
319 {
320 constexpr uint8_t addr = 0xC;
321 int ret = i2c_smbus_read_byte_data(file, addr);
322 if (ret < 0)
323 {
324 std::cerr << "Error " << __FUNCTION__ << "\n";
325 return false;
326 }
327 val = static_cast<uint8_t>(ret);
328 return true;
329 }
330
331 bool getRebuild(uint8_t& val)
332 {
333 constexpr uint8_t addr = 0xD;
334 int ret = i2c_smbus_read_byte_data(file, addr);
335 if (ret < 0)
336 {
337 std::cerr << "Error " << __FUNCTION__ << "\n";
338 return false;
339 }
340 val = static_cast<uint8_t>(ret);
341 return true;
342 }
343
Feist, Jamesc95cf672019-08-29 16:10:35 -0700344 ~Backplane()
345 {
346 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700347 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700348 if (file >= 0)
349 {
350 close(file);
351 }
352 }
353
354 size_t bus;
355 size_t address;
James Feist45772222019-09-27 10:38:08 -0700356 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700357 std::string name;
James Feist45772222019-09-27 10:38:08 -0700358 std::shared_ptr<boost::asio::steady_timer> timer;
359 bool present = false;
360 uint8_t typeId = 0;
361 uint8_t bootVer = 0;
362 uint8_t fpgaVer = 0;
363 uint8_t securityRev = 0;
364 uint8_t funSupported = 0;
365 uint8_t presence = 0;
366 uint8_t ifdet = 0;
367 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700368 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700369
370 int file = -1;
371
Feist, Jamesc95cf672019-08-29 16:10:35 -0700372 std::string type;
373
374 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700375 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
376
377 std::vector<Drive> drives;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700378};
379
380std::unordered_map<std::string, Backplane> backplanes;
381
382void populate()
383{
384 conn->async_method_call(
385 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
386 if (ec)
387 {
388 std::cerr << "Error contacting mapper " << ec.message() << "\n";
389 return;
390 }
391 for (const auto& [path, objDict] : subtree)
392 {
393 if (objDict.empty())
394 {
395 continue;
396 }
397
398 const std::string& owner = objDict.begin()->first;
399 conn->async_method_call(
400 [path](const boost::system::error_code ec2,
401 const boost::container::flat_map<
402 std::string, BasicVariantType>& resp) {
403 if (ec2)
404 {
405 std::cerr << "Error Getting Config "
406 << ec2.message() << "\n";
407 return;
408 }
409 backplanes.clear();
410 std::optional<size_t> bus;
411 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700412 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700413 std::optional<std::string> name;
414 for (const auto& [key, value] : resp)
415 {
416 if (key == "Bus")
417 {
418 bus = std::get<uint64_t>(value);
419 }
420 else if (key == "Address")
421 {
422 address = std::get<uint64_t>(value);
423 }
James Feist45772222019-09-27 10:38:08 -0700424 else if (key == "Index")
425 {
426 backplaneIndex = std::get<uint64_t>(value);
427 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700428 else if (key == "Name")
429 {
430 name = std::get<std::string>(value);
431 }
432 }
James Feist45772222019-09-27 10:38:08 -0700433 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700434 {
435 std::cerr << "Illegal configuration at " << path
436 << "\n";
437 return;
438 }
439 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -0700440 *name,
441 Backplane(*bus, *address, *backplaneIndex, *name));
Feist, Jamesc95cf672019-08-29 16:10:35 -0700442 backplane->second.run();
443 },
444 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
445 configType);
446 }
447 },
448 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
449 0, std::array<const char*, 1>{configType});
450}
451
452int main()
453{
454 boost::asio::steady_timer callbackTimer(io);
455
456 conn->request_name("xyz.openbmc_project.HsbpManager");
457
458 sdbusplus::bus::match::match match(
459 *conn,
460 "type='signal',member='PropertiesChanged',arg0='" +
461 std::string(configType) + "'",
462 [&callbackTimer](sdbusplus::message::message&) {
463 callbackTimer.expires_after(std::chrono::seconds(2));
464 callbackTimer.async_wait([](const boost::system::error_code ec) {
465 if (ec == boost::asio::error::operation_aborted)
466 {
467 // timer was restarted
468 return;
469 }
470 else if (ec)
471 {
472 std::cerr << "Timer error" << ec.message() << "\n";
473 return;
474 }
475 populate();
476 });
477 });
478
479 io.post([]() { populate(); });
480 io.run();
481}