blob: 59a0d85a3ec4c6366cf485935f1e26657ff81568 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM 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 */
Matt Spinlercad9c2b2019-12-02 15:42:01 -060016#include "config.h"
17
Matt Spinlerc8705e22019-09-11 12:36:07 -050018#include "data_interface.hpp"
19
Matt Spinlerf61f2922020-06-23 11:32:49 -050020#include "util.hpp"
21
Vijay Lobo81b4dca2021-04-29 00:04:00 -050022#include <fmt/format.h>
23
Matt Spinlercad9c2b2019-12-02 15:42:01 -060024#include <fstream>
Vijay Lobo81b4dca2021-04-29 00:04:00 -050025#include <phosphor-logging/log.hpp>
Matt Spinlerf10068d2020-12-02 10:44:08 -060026#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
Matt Spinlera7d9d962019-11-06 15:01:25 -060027
Matt Spinlerc8705e22019-09-11 12:36:07 -050028namespace openpower
29{
30namespace pels
31{
32
33namespace service_name
34{
35constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050036constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050037constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050038} // namespace service_name
39
40namespace object_path
41{
42constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
43constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050044constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis";
Vijay Lobo81b4dca2021-04-29 00:04:00 -050045constexpr auto motherBoardInv =
46 "/xyz/openbmc_project/inventory/system/chassis/motherboard";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060047constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060048constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
49constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060050constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060051constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060052constexpr auto enableHostPELs =
53 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050054constexpr auto vpdManager = "/com/ibm/VPD/Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050055} // namespace object_path
56
57namespace interface
58{
59constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
60constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
61constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlerf10068d2020-12-02 10:44:08 -060062constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
Matt Spinlerb3f51862019-12-09 13:55:10 -060063constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060064constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060065constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
66constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
67constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060068constexpr auto invMotherboard =
69 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
70constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Matt Spinler60c4e792020-03-13 13:45:36 -050071constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinler1ab66962020-10-29 13:21:44 -050072constexpr auto compatible =
73 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050074constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050075constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
Matt Spinler993168d2021-04-07 16:05:03 -050076constexpr auto operationalStatus =
77 "xyz.openbmc_project.State.Decorator.OperationalStatus";
Matt Spinlerc8705e22019-09-11 12:36:07 -050078} // namespace interface
79
Matt Spinlerf10068d2020-12-02 10:44:08 -060080using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060081using sdbusplus::exception::SdBusError;
Vijay Lobo81b4dca2021-04-29 00:04:00 -050082using namespace phosphor::logging;
Matt Spinlera7d9d962019-11-06 15:01:25 -060083
Matt Spinlerc8705e22019-09-11 12:36:07 -050084DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
85{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060086 readBMCFWVersion();
87 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060088 readBMCFWVersionID();
Matt Spinler2a28c932020-02-03 14:23:40 -060089
Matt Spinlerf10068d2020-12-02 10:44:08 -060090 // Watch the BootProgress property
Matt Spinler2a28c932020-02-03 14:23:40 -060091 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
Matt Spinlerf10068d2020-12-02 10:44:08 -060092 bus, object_path::hostState, interface::bootProgress, "BootProgress",
93 *this, [this](const auto& value) {
94 auto status = Progress::convertProgressStagesFromString(
95 std::get<std::string>(value));
Matt Spinler2a28c932020-02-03 14:23:40 -060096
Matt Spinlerf10068d2020-12-02 10:44:08 -060097 if ((status == Progress::ProgressStages::SystemInitComplete) ||
98 (status == Progress::ProgressStages::OSStart) ||
99 (status == Progress::ProgressStages::OSRunning))
Matt Spinler2a28c932020-02-03 14:23:40 -0600100 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600101 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600102 }
103 else
104 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600105 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600106 }
107 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600108
109 // Watch the host PEL enable property
110 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
111 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
112 [this](const auto& value) {
113 this->_sendPELsToHost = std::get<bool>(value);
114 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600115
116 // Watch the BMCState property
117 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
118 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
119 *this, [this](const auto& value) {
120 this->_bmcState = std::get<std::string>(value);
121 }));
122
123 // Watch the chassis current and requested power state properties
124 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
125 bus, object_path::chassisState, interface::chassisState, *this,
126 [this](const auto& properties) {
127 auto state = properties.find("CurrentPowerState");
128 if (state != properties.end())
129 {
130 this->_chassisState = std::get<std::string>(state->second);
131 }
132
133 auto trans = properties.find("RequestedPowerTransition");
134 if (trans != properties.end())
135 {
136 this->_chassisTransition = std::get<std::string>(trans->second);
137 }
138 }));
139
140 // Watch the CurrentHostState property
141 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
142 bus, object_path::hostState, interface::hostState, "CurrentHostState",
143 *this, [this](const auto& value) {
144 this->_hostState = std::get<std::string>(value);
145 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500146}
147
Matt Spinler2a28c932020-02-03 14:23:40 -0600148DBusPropertyMap
149 DataInterface::getAllProperties(const std::string& service,
150 const std::string& objectPath,
151 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500152{
153 DBusPropertyMap properties;
154
155 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
156 interface::dbusProperty, "GetAll");
157 method.append(interface);
158 auto reply = _bus.call(method);
159
160 reply.read(properties);
161
162 return properties;
163}
164
Matt Spinlera7d9d962019-11-06 15:01:25 -0600165void DataInterface::getProperty(const std::string& service,
166 const std::string& objectPath,
167 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600168 const std::string& property,
169 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600170{
171
172 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
173 interface::dbusProperty, "Get");
174 method.append(interface, property);
175 auto reply = _bus.call(method);
176
177 reply.read(value);
178}
179
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600180DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
181{
182
183 auto method = _bus.new_method_call(
184 service_name::objectMapper, object_path::objectMapper,
185 interface::objectMapper, "GetSubTreePaths");
186
187 method.append(std::string{"/"}, 0, interfaces);
188
189 auto reply = _bus.call(method);
190
191 DBusPathList paths;
192 reply.read(paths);
193
194 return paths;
195}
196
Matt Spinlerc8705e22019-09-11 12:36:07 -0500197DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600198 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500199{
200 auto method = _bus.new_method_call(service_name::objectMapper,
201 object_path::objectMapper,
202 interface::objectMapper, "GetObject");
203
204 method.append(objectPath, std::vector<std::string>({interface}));
205
206 auto reply = _bus.call(method);
207
208 std::map<DBusService, DBusInterfaceList> response;
209 reply.read(response);
210
211 if (!response.empty())
212 {
213 return response.begin()->first;
214 }
215
216 return std::string{};
217}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600218
Matt Spinler677381b2020-01-23 10:04:29 -0600219void DataInterface::readBMCFWVersion()
220{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500221 _bmcFWVersion =
222 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600223}
224
225void DataInterface::readServerFWVersion()
226{
Sumit Kumarcad16202021-05-13 04:06:15 -0500227 auto value =
228 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
229 if ((value != "") && (value.find_last_of(')') != std::string::npos))
230 {
231 std::size_t pos = value.find_first_of('(') + 1;
232 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
233 }
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600234}
235
Matt Spinler677381b2020-01-23 10:04:29 -0600236void DataInterface::readBMCFWVersionID()
237{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500238 _bmcFWVersionID =
239 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600240}
241
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500242std::string DataInterface::getMachineTypeModel() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600243{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500244 std::string model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600245 try
246 {
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600247
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500248 auto service = getService(object_path::systemInv, interface::invAsset);
249 if (!service.empty())
250 {
251 DBusValue value;
252 getProperty(service, object_path::systemInv, interface::invAsset,
253 "Model", value);
254
255 model = std::get<std::string>(value);
256 }
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600257 }
258 catch (const std::exception& e)
259 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500260 log<level::WARNING>(fmt::format("Failed reading Model property from "
261 "Interface: {} exception: {}",
262 interface::invAsset, e.what())
263 .c_str());
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600264 }
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500265
266 return model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600267}
268
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500269std::string DataInterface::getMachineSerialNumber() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600270{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500271 std::string sn;
272 try
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600273 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500274
275 auto service = getService(object_path::systemInv, interface::invAsset);
276 if (!service.empty())
277 {
278 DBusValue value;
279 getProperty(service, object_path::systemInv, interface::invAsset,
280 "SerialNumber", value);
281
282 sn = std::get<std::string>(value);
283 }
284 }
285 catch (const std::exception& e)
286 {
287 log<level::WARNING>(
288 fmt::format("Failed reading SerialNumber property from "
289 "Interface: {} exception: {}",
290 interface::invAsset, e.what())
291 .c_str());
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600292 }
293
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500294 return sn;
295}
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600296
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500297std::string DataInterface::getMotherboardCCIN() const
298{
299 std::string ccin;
300
301 try
302 {
303 auto service =
304 getService(object_path::motherBoardInv, interface::viniRecordVPD);
305 if (!service.empty())
306 {
307 DBusValue value;
308 getProperty(service, object_path::motherBoardInv,
309 interface::viniRecordVPD, "CC", value);
310
311 auto cc = std::get<std::vector<uint8_t>>(value);
312 ccin = std::string{cc.begin(), cc.end()};
313 }
314 }
315 catch (const std::exception& e)
316 {
317 log<level::WARNING>(
318 fmt::format("Failed reading Motherboard CCIN property from "
319 "Interface: {} exception: {}",
320 interface::viniRecordVPD, e.what())
321 .c_str());
322 }
323
324 return ccin;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600325}
326
Matt Spinler60c4e792020-03-13 13:45:36 -0500327void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500328 std::string& fruPartNumber,
329 std::string& ccin,
330 std::string& serialNumber) const
331{
332 // For now, attempt to get all of the properties directly on the path
333 // passed in. In the future, may need to make use of an algorithm
334 // to figure out which inventory objects actually hold these
335 // interfaces in the case of non FRUs, or possibly another service
336 // will provide this info. Any missing interfaces will result
337 // in exceptions being thrown.
338
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500339 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500340
341 auto properties =
342 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
343
344 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
345 fruPartNumber = std::string{value.begin(), value.end()};
346
347 value = std::get<std::vector<uint8_t>>(properties["CC"]);
348 ccin = std::string{value.begin(), value.end()};
349
350 value = std::get<std::vector<uint8_t>>(properties["SN"]);
351 serialNumber = std::string{value.begin(), value.end()};
352}
353
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500354std::string
355 DataInterface::getLocationCode(const std::string& inventoryPath) const
356{
357 auto service = getService(inventoryPath, interface::locCode);
358
359 DBusValue locCode;
360 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
361 locCode);
362
363 return std::get<std::string>(locCode);
364}
365
Matt Spinler5fb24c12020-06-04 11:21:33 -0500366std::string
367 DataInterface::addLocationCodePrefix(const std::string& locationCode)
368{
369 static const std::string locationCodePrefix{"Ufcs-"};
370
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500371 // Technically there are 2 location code prefixes, Ufcs and Umts, so
372 // if it already starts with a U then don't need to do anything.
373 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500374 {
375 return locationCodePrefix + locationCode;
376 }
377
378 return locationCode;
379}
380
381std::string DataInterface::expandLocationCode(const std::string& locationCode,
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500382 uint16_t /*node*/) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500383{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500384 auto method =
385 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
386 interface::vpdManager, "GetExpandedLocationCode");
387
388 method.append(addLocationCodePrefix(locationCode),
389 static_cast<uint16_t>(0));
390
391 auto reply = _bus.call(method);
392
393 std::string expandedLocationCode;
394 reply.read(expandedLocationCode);
395
396 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500397}
398
Matt Spinler2f9225a2020-08-05 12:58:49 -0500399std::string
400 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
401 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500402{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500403 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
404 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500405
Matt Spinler2f9225a2020-08-05 12:58:49 -0500406 auto method =
407 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
408 interface::vpdManager, methodName.c_str());
409
410 if (expanded)
411 {
412 method.append(locationCode);
413 }
414 else
415 {
416 method.append(addLocationCodePrefix(locationCode), node);
417 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500418
419 auto reply = _bus.call(method);
420
421 std::vector<sdbusplus::message::object_path> entries;
422 reply.read(entries);
423
424 // Get the shortest entry from the paths received, as this
425 // would be the path furthest up the inventory hierarchy so
426 // would be the parent FRU. There is guaranteed to at least
427 // be one entry if the call didn't fail.
428 std::string shortest{entries[0]};
429
430 std::for_each(entries.begin(), entries.end(),
431 [&shortest](const auto& path) {
432 if (path.str.size() < shortest.size())
433 {
434 shortest = path;
435 }
436 });
437
438 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500439}
440
Matt Spinler34a904c2020-08-05 14:53:28 -0500441void DataInterface::assertLEDGroup(const std::string& ledGroup,
442 bool value) const
443{
444 DBusValue variant = value;
445 auto method =
446 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
447 interface::dbusProperty, "Set");
448 method.append(interface::ledGroup, "Asserted", variant);
449 _bus.call(method);
450}
451
Matt Spinler993168d2021-04-07 16:05:03 -0500452void DataInterface::setFunctional(const std::string& objectPath,
453 bool value) const
454{
455 DBusValue variant = value;
456 auto service = getService(objectPath, interface::operationalStatus);
457
458 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
459 interface::dbusProperty, "Set");
460
461 method.append(interface::operationalStatus, "Functional", variant);
462 _bus.call(method);
463}
464
Matt Spinler1ab66962020-10-29 13:21:44 -0500465std::vector<std::string> DataInterface::getSystemNames() const
466{
467 DBusSubTree subtree;
468 DBusValue names;
469
470 auto method = _bus.new_method_call(service_name::objectMapper,
471 object_path::objectMapper,
472 interface::objectMapper, "GetSubTree");
473 method.append(std::string{"/"}, 0,
474 std::vector<std::string>{interface::compatible});
475 auto reply = _bus.call(method);
476
477 reply.read(subtree);
478 if (subtree.empty())
479 {
480 throw std::runtime_error("Compatible interface not on D-Bus");
481 }
482
483 const auto& object = *(subtree.begin());
484 const auto& path = object.first;
485 const auto& service = object.second.begin()->first;
486
487 getProperty(service, path, interface::compatible, "Names", names);
488
489 return std::get<std::vector<std::string>>(names);
490}
491
Matt Spinlerc8705e22019-09-11 12:36:07 -0500492} // namespace pels
493} // namespace openpower