blob: de6591ace10fc8d3538075e5de857d897f10ab2f [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 Spinlera7d9d962019-11-06 15:01:25 -060023#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
24
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 Spinlera7d9d962019-11-06 15:01:25 -060057constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status";
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 association = "xyz.openbmc_project.Association";
71constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
Matt Spinlerc8705e22019-09-11 12:36:07 -050072} // namespace interface
73
Matt Spinlera7d9d962019-11-06 15:01:25 -060074using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060075using sdbusplus::exception::SdBusError;
Matt Spinlera7d9d962019-11-06 15:01:25 -060076
Matt Spinlerc8705e22019-09-11 12:36:07 -050077DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
78{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060079 readBMCFWVersion();
80 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060081 readBMCFWVersionID();
Matt Spinlerb3d488f2020-02-21 15:30:46 -060082 readMotherboardCCIN();
Matt Spinler2a28c932020-02-03 14:23:40 -060083
84 // Watch both the Model and SN properties on the system's Asset iface
85 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
86 bus, object_path::systemInv, interface::invAsset, *this,
87 [this](const auto& properties) {
88 auto model = properties.find("Model");
89 if (model != properties.end())
90 {
91 this->_machineTypeModel = std::get<std::string>(model->second);
92 }
93
94 auto sn = properties.find("SerialNumber");
95 if (sn != properties.end())
96 {
97 this->_machineSerialNumber = std::get<std::string>(sn->second);
98 }
99 }));
100
101 // Watch the OperatingSystemState property
102 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
103 bus, object_path::hostState, interface::osStatus,
104 "OperatingSystemState", *this, [this](const auto& value) {
105 auto status =
106 Status::convertOSStatusFromString(std::get<std::string>(value));
107
108 if ((status == Status::OSStatus::BootComplete) ||
109 (status == Status::OSStatus::Standby))
110 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600111 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600112 }
113 else
114 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600115 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600116 }
117 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600118
119 // Watch the host PEL enable property
120 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
121 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
122 [this](const auto& value) {
123 this->_sendPELsToHost = std::get<bool>(value);
124 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600125
126 // Watch the BMCState property
127 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
128 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
129 *this, [this](const auto& value) {
130 this->_bmcState = std::get<std::string>(value);
131 }));
132
133 // Watch the chassis current and requested power state properties
134 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
135 bus, object_path::chassisState, interface::chassisState, *this,
136 [this](const auto& properties) {
137 auto state = properties.find("CurrentPowerState");
138 if (state != properties.end())
139 {
140 this->_chassisState = std::get<std::string>(state->second);
141 }
142
143 auto trans = properties.find("RequestedPowerTransition");
144 if (trans != properties.end())
145 {
146 this->_chassisTransition = std::get<std::string>(trans->second);
147 }
148 }));
149
150 // Watch the CurrentHostState property
151 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
152 bus, object_path::hostState, interface::hostState, "CurrentHostState",
153 *this, [this](const auto& value) {
154 this->_hostState = std::get<std::string>(value);
155 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500156}
157
Matt Spinler2a28c932020-02-03 14:23:40 -0600158DBusPropertyMap
159 DataInterface::getAllProperties(const std::string& service,
160 const std::string& objectPath,
161 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500162{
163 DBusPropertyMap properties;
164
165 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
166 interface::dbusProperty, "GetAll");
167 method.append(interface);
168 auto reply = _bus.call(method);
169
170 reply.read(properties);
171
172 return properties;
173}
174
Matt Spinlera7d9d962019-11-06 15:01:25 -0600175void DataInterface::getProperty(const std::string& service,
176 const std::string& objectPath,
177 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600178 const std::string& property,
179 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600180{
181
182 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
183 interface::dbusProperty, "Get");
184 method.append(interface, property);
185 auto reply = _bus.call(method);
186
187 reply.read(value);
188}
189
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600190DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
191{
192
193 auto method = _bus.new_method_call(
194 service_name::objectMapper, object_path::objectMapper,
195 interface::objectMapper, "GetSubTreePaths");
196
197 method.append(std::string{"/"}, 0, interfaces);
198
199 auto reply = _bus.call(method);
200
201 DBusPathList paths;
202 reply.read(paths);
203
204 return paths;
205}
206
Matt Spinlerc8705e22019-09-11 12:36:07 -0500207DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600208 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500209{
210 auto method = _bus.new_method_call(service_name::objectMapper,
211 object_path::objectMapper,
212 interface::objectMapper, "GetObject");
213
214 method.append(objectPath, std::vector<std::string>({interface}));
215
216 auto reply = _bus.call(method);
217
218 std::map<DBusService, DBusInterfaceList> response;
219 reply.read(response);
220
221 if (!response.empty())
222 {
223 return response.begin()->first;
224 }
225
226 return std::string{};
227}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600228
Matt Spinler677381b2020-01-23 10:04:29 -0600229void DataInterface::readBMCFWVersion()
230{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500231 _bmcFWVersion =
232 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600233}
234
235void DataInterface::readServerFWVersion()
236{
237 // Not available yet
238}
239
Matt Spinler677381b2020-01-23 10:04:29 -0600240void DataInterface::readBMCFWVersionID()
241{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500242 _bmcFWVersionID =
243 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600244}
245
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600246void DataInterface::readMotherboardCCIN()
247{
248 try
249 {
250 // First, try to find the motherboard
251 auto motherboards = getPaths({interface::invMotherboard});
252 if (motherboards.empty())
253 {
254 throw std::runtime_error("No motherboards yet");
255 }
256
257 // Found it, so now get the CCIN
258 _properties.emplace_back(
259 std::make_unique<PropertyWatcher<DataInterface>>(
260 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
261 *this,
262 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
263 }
264 catch (const std::exception& e)
265 {
266 // No motherboard in the inventory yet - watch for it
267 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
268 _bus, match_rules::interfacesAdded(object_path::baseInv),
269 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
270 std::placeholders::_1));
271 }
272}
273
274void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
275{
276 sdbusplus::message::object_path path;
277 DBusInterfaceMap interfaces;
278
279 msg.read(path, interfaces);
280
281 // This is watching the whole inventory, so check if it's what we want
282 if (interfaces.find(interface::invMotherboard) == interfaces.end())
283 {
284 return;
285 }
286
287 // Done watching for any new inventory interfaces
288 _inventoryIfacesAddedMatch.reset();
289
290 // Watch the motherboard CCIN, using the service from this signal
291 // for the initial property read.
292 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
293 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
294 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
295}
296
Matt Spinler60c4e792020-03-13 13:45:36 -0500297void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500298 std::string& fruPartNumber,
299 std::string& ccin,
300 std::string& serialNumber) const
301{
302 // For now, attempt to get all of the properties directly on the path
303 // passed in. In the future, may need to make use of an algorithm
304 // to figure out which inventory objects actually hold these
305 // interfaces in the case of non FRUs, or possibly another service
306 // will provide this info. Any missing interfaces will result
307 // in exceptions being thrown.
308
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500309 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500310
311 auto properties =
312 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
313
314 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
315 fruPartNumber = std::string{value.begin(), value.end()};
316
317 value = std::get<std::vector<uint8_t>>(properties["CC"]);
318 ccin = std::string{value.begin(), value.end()};
319
320 value = std::get<std::vector<uint8_t>>(properties["SN"]);
321 serialNumber = std::string{value.begin(), value.end()};
322}
323
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500324std::string
325 DataInterface::getLocationCode(const std::string& inventoryPath) const
326{
327 auto service = getService(inventoryPath, interface::locCode);
328
329 DBusValue locCode;
330 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
331 locCode);
332
333 return std::get<std::string>(locCode);
334}
335
Matt Spinler5fb24c12020-06-04 11:21:33 -0500336std::string
337 DataInterface::addLocationCodePrefix(const std::string& locationCode)
338{
339 static const std::string locationCodePrefix{"Ufcs-"};
340
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500341 // Technically there are 2 location code prefixes, Ufcs and Umts, so
342 // if it already starts with a U then don't need to do anything.
343 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500344 {
345 return locationCodePrefix + locationCode;
346 }
347
348 return locationCode;
349}
350
351std::string DataInterface::expandLocationCode(const std::string& locationCode,
352 uint16_t node) const
353{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500354 auto method =
355 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
356 interface::vpdManager, "GetExpandedLocationCode");
357
358 method.append(addLocationCodePrefix(locationCode),
359 static_cast<uint16_t>(0));
360
361 auto reply = _bus.call(method);
362
363 std::string expandedLocationCode;
364 reply.read(expandedLocationCode);
365
366 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500367}
368
Matt Spinler2f9225a2020-08-05 12:58:49 -0500369std::string
370 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
371 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500372{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500373 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
374 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500375
Matt Spinler2f9225a2020-08-05 12:58:49 -0500376 auto method =
377 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
378 interface::vpdManager, methodName.c_str());
379
380 if (expanded)
381 {
382 method.append(locationCode);
383 }
384 else
385 {
386 method.append(addLocationCodePrefix(locationCode), node);
387 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500388
389 auto reply = _bus.call(method);
390
391 std::vector<sdbusplus::message::object_path> entries;
392 reply.read(entries);
393
394 // Get the shortest entry from the paths received, as this
395 // would be the path furthest up the inventory hierarchy so
396 // would be the parent FRU. There is guaranteed to at least
397 // be one entry if the call didn't fail.
398 std::string shortest{entries[0]};
399
400 std::for_each(entries.begin(), entries.end(),
401 [&shortest](const auto& path) {
402 if (path.str.size() < shortest.size())
403 {
404 shortest = path;
405 }
406 });
407
408 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500409}
410
Matt Spinler34a904c2020-08-05 14:53:28 -0500411std::string
412 DataInterface::getFaultLEDGroup(const std::string& inventoryPath) const
413{
414 auto associationPath = inventoryPath + "/" + "fault_led_group";
415 auto service = getService(associationPath, interface::association);
416
417 DBusValue endpoints;
418 getProperty(service, associationPath, interface::association, "endpoints",
419 endpoints);
420 auto paths = std::get<std::vector<std::string>>(endpoints);
421 if (paths.empty())
422 {
423 throw std::runtime_error("Association endpoints property empty");
424 }
425
426 return paths[0];
427}
428
429void DataInterface::assertLEDGroup(const std::string& ledGroup,
430 bool value) const
431{
432 DBusValue variant = value;
433 auto method =
434 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
435 interface::dbusProperty, "Set");
436 method.append(interface::ledGroup, "Asserted", variant);
437 _bus.call(method);
438}
439
Matt Spinler1ab66962020-10-29 13:21:44 -0500440std::vector<std::string> DataInterface::getSystemNames() const
441{
442 DBusSubTree subtree;
443 DBusValue names;
444
445 auto method = _bus.new_method_call(service_name::objectMapper,
446 object_path::objectMapper,
447 interface::objectMapper, "GetSubTree");
448 method.append(std::string{"/"}, 0,
449 std::vector<std::string>{interface::compatible});
450 auto reply = _bus.call(method);
451
452 reply.read(subtree);
453 if (subtree.empty())
454 {
455 throw std::runtime_error("Compatible interface not on D-Bus");
456 }
457
458 const auto& object = *(subtree.begin());
459 const auto& path = object.first;
460 const auto& service = object.second.begin()->first;
461
462 getProperty(service, path, interface::compatible, "Names", names);
463
464 return std::get<std::vector<std::string>>(names);
465}
466
Matt Spinlerc8705e22019-09-11 12:36:07 -0500467} // namespace pels
468} // namespace openpower