blob: a228cbb7bfb672ce7333c7039fa9447287bf9eb9 [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
Matt Spinlercad9c2b2019-12-02 15:42:01 -060022#include <fstream>
Matt Spinlerf10068d2020-12-02 10:44:08 -060023#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
Matt Spinlera7d9d962019-11-06 15:01:25 -060024
Matt Spinlerc8705e22019-09-11 12:36:07 -050025namespace openpower
26{
27namespace pels
28{
29
30namespace service_name
31{
32constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050033constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050034constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050035} // namespace service_name
36
37namespace object_path
38{
39constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
40constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050041constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060042constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060043constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
44constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060045constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060046constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060047constexpr auto enableHostPELs =
48 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050049constexpr auto vpdManager = "/com/ibm/VPD/Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050050} // namespace object_path
51
52namespace interface
53{
54constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
55constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
56constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlerf10068d2020-12-02 10:44:08 -060057constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
Matt Spinlerb3f51862019-12-09 13:55:10 -060058constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060059constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060060constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
61constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
62constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060063constexpr auto invMotherboard =
64 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
65constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Matt Spinler60c4e792020-03-13 13:45:36 -050066constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinler1ab66962020-10-29 13:21:44 -050067constexpr auto compatible =
68 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050069constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050070constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
Matt Spinler993168d2021-04-07 16:05:03 -050071constexpr auto operationalStatus =
72 "xyz.openbmc_project.State.Decorator.OperationalStatus";
Matt Spinlerc8705e22019-09-11 12:36:07 -050073} // namespace interface
74
Matt Spinlerf10068d2020-12-02 10:44:08 -060075using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060076using sdbusplus::exception::SdBusError;
Matt Spinlera7d9d962019-11-06 15:01:25 -060077
Matt Spinlerc8705e22019-09-11 12:36:07 -050078DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
79{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060080 readBMCFWVersion();
81 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060082 readBMCFWVersionID();
Matt Spinlerb3d488f2020-02-21 15:30:46 -060083 readMotherboardCCIN();
Matt Spinler2a28c932020-02-03 14:23:40 -060084
85 // Watch both the Model and SN properties on the system's Asset iface
86 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
87 bus, object_path::systemInv, interface::invAsset, *this,
88 [this](const auto& properties) {
89 auto model = properties.find("Model");
90 if (model != properties.end())
91 {
92 this->_machineTypeModel = std::get<std::string>(model->second);
93 }
94
95 auto sn = properties.find("SerialNumber");
96 if (sn != properties.end())
97 {
98 this->_machineSerialNumber = std::get<std::string>(sn->second);
99 }
100 }));
101
Matt Spinlerf10068d2020-12-02 10:44:08 -0600102 // Watch the BootProgress property
Matt Spinler2a28c932020-02-03 14:23:40 -0600103 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
Matt Spinlerf10068d2020-12-02 10:44:08 -0600104 bus, object_path::hostState, interface::bootProgress, "BootProgress",
105 *this, [this](const auto& value) {
106 auto status = Progress::convertProgressStagesFromString(
107 std::get<std::string>(value));
Matt Spinler2a28c932020-02-03 14:23:40 -0600108
Matt Spinlerf10068d2020-12-02 10:44:08 -0600109 if ((status == Progress::ProgressStages::SystemInitComplete) ||
110 (status == Progress::ProgressStages::OSStart) ||
111 (status == Progress::ProgressStages::OSRunning))
Matt Spinler2a28c932020-02-03 14:23:40 -0600112 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600113 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600114 }
115 else
116 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600117 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600118 }
119 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600120
121 // Watch the host PEL enable property
122 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
123 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
124 [this](const auto& value) {
125 this->_sendPELsToHost = std::get<bool>(value);
126 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600127
128 // Watch the BMCState property
129 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
130 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
131 *this, [this](const auto& value) {
132 this->_bmcState = std::get<std::string>(value);
133 }));
134
135 // Watch the chassis current and requested power state properties
136 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
137 bus, object_path::chassisState, interface::chassisState, *this,
138 [this](const auto& properties) {
139 auto state = properties.find("CurrentPowerState");
140 if (state != properties.end())
141 {
142 this->_chassisState = std::get<std::string>(state->second);
143 }
144
145 auto trans = properties.find("RequestedPowerTransition");
146 if (trans != properties.end())
147 {
148 this->_chassisTransition = std::get<std::string>(trans->second);
149 }
150 }));
151
152 // Watch the CurrentHostState property
153 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
154 bus, object_path::hostState, interface::hostState, "CurrentHostState",
155 *this, [this](const auto& value) {
156 this->_hostState = std::get<std::string>(value);
157 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500158}
159
Matt Spinler2a28c932020-02-03 14:23:40 -0600160DBusPropertyMap
161 DataInterface::getAllProperties(const std::string& service,
162 const std::string& objectPath,
163 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500164{
165 DBusPropertyMap properties;
166
167 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
168 interface::dbusProperty, "GetAll");
169 method.append(interface);
170 auto reply = _bus.call(method);
171
172 reply.read(properties);
173
174 return properties;
175}
176
Matt Spinlera7d9d962019-11-06 15:01:25 -0600177void DataInterface::getProperty(const std::string& service,
178 const std::string& objectPath,
179 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600180 const std::string& property,
181 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600182{
183
184 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
185 interface::dbusProperty, "Get");
186 method.append(interface, property);
187 auto reply = _bus.call(method);
188
189 reply.read(value);
190}
191
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600192DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
193{
194
195 auto method = _bus.new_method_call(
196 service_name::objectMapper, object_path::objectMapper,
197 interface::objectMapper, "GetSubTreePaths");
198
199 method.append(std::string{"/"}, 0, interfaces);
200
201 auto reply = _bus.call(method);
202
203 DBusPathList paths;
204 reply.read(paths);
205
206 return paths;
207}
208
Matt Spinlerc8705e22019-09-11 12:36:07 -0500209DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600210 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500211{
212 auto method = _bus.new_method_call(service_name::objectMapper,
213 object_path::objectMapper,
214 interface::objectMapper, "GetObject");
215
216 method.append(objectPath, std::vector<std::string>({interface}));
217
218 auto reply = _bus.call(method);
219
220 std::map<DBusService, DBusInterfaceList> response;
221 reply.read(response);
222
223 if (!response.empty())
224 {
225 return response.begin()->first;
226 }
227
228 return std::string{};
229}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600230
Matt Spinler677381b2020-01-23 10:04:29 -0600231void DataInterface::readBMCFWVersion()
232{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500233 _bmcFWVersion =
234 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600235}
236
237void DataInterface::readServerFWVersion()
238{
239 // Not available yet
240}
241
Matt Spinler677381b2020-01-23 10:04:29 -0600242void DataInterface::readBMCFWVersionID()
243{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500244 _bmcFWVersionID =
245 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600246}
247
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600248void DataInterface::readMotherboardCCIN()
249{
250 try
251 {
252 // First, try to find the motherboard
253 auto motherboards = getPaths({interface::invMotherboard});
254 if (motherboards.empty())
255 {
256 throw std::runtime_error("No motherboards yet");
257 }
258
259 // Found it, so now get the CCIN
260 _properties.emplace_back(
261 std::make_unique<PropertyWatcher<DataInterface>>(
262 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
263 *this,
264 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
265 }
266 catch (const std::exception& e)
267 {
268 // No motherboard in the inventory yet - watch for it
269 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
270 _bus, match_rules::interfacesAdded(object_path::baseInv),
271 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
272 std::placeholders::_1));
273 }
274}
275
276void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
277{
278 sdbusplus::message::object_path path;
279 DBusInterfaceMap interfaces;
280
281 msg.read(path, interfaces);
282
283 // This is watching the whole inventory, so check if it's what we want
284 if (interfaces.find(interface::invMotherboard) == interfaces.end())
285 {
286 return;
287 }
288
289 // Done watching for any new inventory interfaces
290 _inventoryIfacesAddedMatch.reset();
291
292 // Watch the motherboard CCIN, using the service from this signal
293 // for the initial property read.
294 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
295 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
296 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
297}
298
Matt Spinler60c4e792020-03-13 13:45:36 -0500299void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500300 std::string& fruPartNumber,
301 std::string& ccin,
302 std::string& serialNumber) const
303{
304 // For now, attempt to get all of the properties directly on the path
305 // passed in. In the future, may need to make use of an algorithm
306 // to figure out which inventory objects actually hold these
307 // interfaces in the case of non FRUs, or possibly another service
308 // will provide this info. Any missing interfaces will result
309 // in exceptions being thrown.
310
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500311 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500312
313 auto properties =
314 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
315
316 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
317 fruPartNumber = std::string{value.begin(), value.end()};
318
319 value = std::get<std::vector<uint8_t>>(properties["CC"]);
320 ccin = std::string{value.begin(), value.end()};
321
322 value = std::get<std::vector<uint8_t>>(properties["SN"]);
323 serialNumber = std::string{value.begin(), value.end()};
324}
325
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500326std::string
327 DataInterface::getLocationCode(const std::string& inventoryPath) const
328{
329 auto service = getService(inventoryPath, interface::locCode);
330
331 DBusValue locCode;
332 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
333 locCode);
334
335 return std::get<std::string>(locCode);
336}
337
Matt Spinler5fb24c12020-06-04 11:21:33 -0500338std::string
339 DataInterface::addLocationCodePrefix(const std::string& locationCode)
340{
341 static const std::string locationCodePrefix{"Ufcs-"};
342
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500343 // Technically there are 2 location code prefixes, Ufcs and Umts, so
344 // if it already starts with a U then don't need to do anything.
345 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500346 {
347 return locationCodePrefix + locationCode;
348 }
349
350 return locationCode;
351}
352
353std::string DataInterface::expandLocationCode(const std::string& locationCode,
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500354 uint16_t /*node*/) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500355{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500356 auto method =
357 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
358 interface::vpdManager, "GetExpandedLocationCode");
359
360 method.append(addLocationCodePrefix(locationCode),
361 static_cast<uint16_t>(0));
362
363 auto reply = _bus.call(method);
364
365 std::string expandedLocationCode;
366 reply.read(expandedLocationCode);
367
368 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500369}
370
Matt Spinler2f9225a2020-08-05 12:58:49 -0500371std::string
372 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
373 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500374{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500375 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
376 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500377
Matt Spinler2f9225a2020-08-05 12:58:49 -0500378 auto method =
379 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
380 interface::vpdManager, methodName.c_str());
381
382 if (expanded)
383 {
384 method.append(locationCode);
385 }
386 else
387 {
388 method.append(addLocationCodePrefix(locationCode), node);
389 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500390
391 auto reply = _bus.call(method);
392
393 std::vector<sdbusplus::message::object_path> entries;
394 reply.read(entries);
395
396 // Get the shortest entry from the paths received, as this
397 // would be the path furthest up the inventory hierarchy so
398 // would be the parent FRU. There is guaranteed to at least
399 // be one entry if the call didn't fail.
400 std::string shortest{entries[0]};
401
402 std::for_each(entries.begin(), entries.end(),
403 [&shortest](const auto& path) {
404 if (path.str.size() < shortest.size())
405 {
406 shortest = path;
407 }
408 });
409
410 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500411}
412
Matt Spinler34a904c2020-08-05 14:53:28 -0500413void DataInterface::assertLEDGroup(const std::string& ledGroup,
414 bool value) const
415{
416 DBusValue variant = value;
417 auto method =
418 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
419 interface::dbusProperty, "Set");
420 method.append(interface::ledGroup, "Asserted", variant);
421 _bus.call(method);
422}
423
Matt Spinler993168d2021-04-07 16:05:03 -0500424void DataInterface::setFunctional(const std::string& objectPath,
425 bool value) const
426{
427 DBusValue variant = value;
428 auto service = getService(objectPath, interface::operationalStatus);
429
430 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
431 interface::dbusProperty, "Set");
432
433 method.append(interface::operationalStatus, "Functional", variant);
434 _bus.call(method);
435}
436
Matt Spinler1ab66962020-10-29 13:21:44 -0500437std::vector<std::string> DataInterface::getSystemNames() const
438{
439 DBusSubTree subtree;
440 DBusValue names;
441
442 auto method = _bus.new_method_call(service_name::objectMapper,
443 object_path::objectMapper,
444 interface::objectMapper, "GetSubTree");
445 method.append(std::string{"/"}, 0,
446 std::vector<std::string>{interface::compatible});
447 auto reply = _bus.call(method);
448
449 reply.read(subtree);
450 if (subtree.empty())
451 {
452 throw std::runtime_error("Compatible interface not on D-Bus");
453 }
454
455 const auto& object = *(subtree.begin());
456 const auto& path = object.first;
457 const auto& service = object.second.begin()->first;
458
459 getProperty(service, path, interface::compatible, "Names", names);
460
461 return std::get<std::vector<std::string>>(names);
462}
463
Matt Spinlerc8705e22019-09-11 12:36:07 -0500464} // namespace pels
465} // namespace openpower