blob: 518df8f63b9da728b1ef9515cc1733263b61a8dc [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 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 Spinlerf10068d2020-12-02 10:44:08 -060074using namespace sdbusplus::xyz::openbmc_project::State::Boot::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
Matt Spinlerf10068d2020-12-02 10:44:08 -0600101 // Watch the BootProgress property
Matt Spinler2a28c932020-02-03 14:23:40 -0600102 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
Matt Spinlerf10068d2020-12-02 10:44:08 -0600103 bus, object_path::hostState, interface::bootProgress, "BootProgress",
104 *this, [this](const auto& value) {
105 auto status = Progress::convertProgressStagesFromString(
106 std::get<std::string>(value));
Matt Spinler2a28c932020-02-03 14:23:40 -0600107
Matt Spinlerf10068d2020-12-02 10:44:08 -0600108 if ((status == Progress::ProgressStages::SystemInitComplete) ||
109 (status == Progress::ProgressStages::OSStart) ||
110 (status == Progress::ProgressStages::OSRunning))
Matt Spinler2a28c932020-02-03 14:23:40 -0600111 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600112 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600113 }
114 else
115 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600116 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600117 }
118 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600119
120 // Watch the host PEL enable property
121 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
122 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
123 [this](const auto& value) {
124 this->_sendPELsToHost = std::get<bool>(value);
125 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600126
127 // Watch the BMCState property
128 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
129 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
130 *this, [this](const auto& value) {
131 this->_bmcState = std::get<std::string>(value);
132 }));
133
134 // Watch the chassis current and requested power state properties
135 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
136 bus, object_path::chassisState, interface::chassisState, *this,
137 [this](const auto& properties) {
138 auto state = properties.find("CurrentPowerState");
139 if (state != properties.end())
140 {
141 this->_chassisState = std::get<std::string>(state->second);
142 }
143
144 auto trans = properties.find("RequestedPowerTransition");
145 if (trans != properties.end())
146 {
147 this->_chassisTransition = std::get<std::string>(trans->second);
148 }
149 }));
150
151 // Watch the CurrentHostState property
152 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
153 bus, object_path::hostState, interface::hostState, "CurrentHostState",
154 *this, [this](const auto& value) {
155 this->_hostState = std::get<std::string>(value);
156 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500157}
158
Matt Spinler2a28c932020-02-03 14:23:40 -0600159DBusPropertyMap
160 DataInterface::getAllProperties(const std::string& service,
161 const std::string& objectPath,
162 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500163{
164 DBusPropertyMap properties;
165
166 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
167 interface::dbusProperty, "GetAll");
168 method.append(interface);
169 auto reply = _bus.call(method);
170
171 reply.read(properties);
172
173 return properties;
174}
175
Matt Spinlera7d9d962019-11-06 15:01:25 -0600176void DataInterface::getProperty(const std::string& service,
177 const std::string& objectPath,
178 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600179 const std::string& property,
180 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600181{
182
183 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
184 interface::dbusProperty, "Get");
185 method.append(interface, property);
186 auto reply = _bus.call(method);
187
188 reply.read(value);
189}
190
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600191DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
192{
193
194 auto method = _bus.new_method_call(
195 service_name::objectMapper, object_path::objectMapper,
196 interface::objectMapper, "GetSubTreePaths");
197
198 method.append(std::string{"/"}, 0, interfaces);
199
200 auto reply = _bus.call(method);
201
202 DBusPathList paths;
203 reply.read(paths);
204
205 return paths;
206}
207
Matt Spinlerc8705e22019-09-11 12:36:07 -0500208DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600209 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500210{
211 auto method = _bus.new_method_call(service_name::objectMapper,
212 object_path::objectMapper,
213 interface::objectMapper, "GetObject");
214
215 method.append(objectPath, std::vector<std::string>({interface}));
216
217 auto reply = _bus.call(method);
218
219 std::map<DBusService, DBusInterfaceList> response;
220 reply.read(response);
221
222 if (!response.empty())
223 {
224 return response.begin()->first;
225 }
226
227 return std::string{};
228}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600229
Matt Spinler677381b2020-01-23 10:04:29 -0600230void DataInterface::readBMCFWVersion()
231{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500232 _bmcFWVersion =
233 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600234}
235
236void DataInterface::readServerFWVersion()
237{
238 // Not available yet
239}
240
Matt Spinler677381b2020-01-23 10:04:29 -0600241void DataInterface::readBMCFWVersionID()
242{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500243 _bmcFWVersionID =
244 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600245}
246
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600247void DataInterface::readMotherboardCCIN()
248{
249 try
250 {
251 // First, try to find the motherboard
252 auto motherboards = getPaths({interface::invMotherboard});
253 if (motherboards.empty())
254 {
255 throw std::runtime_error("No motherboards yet");
256 }
257
258 // Found it, so now get the CCIN
259 _properties.emplace_back(
260 std::make_unique<PropertyWatcher<DataInterface>>(
261 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
262 *this,
263 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
264 }
265 catch (const std::exception& e)
266 {
267 // No motherboard in the inventory yet - watch for it
268 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
269 _bus, match_rules::interfacesAdded(object_path::baseInv),
270 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
271 std::placeholders::_1));
272 }
273}
274
275void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
276{
277 sdbusplus::message::object_path path;
278 DBusInterfaceMap interfaces;
279
280 msg.read(path, interfaces);
281
282 // This is watching the whole inventory, so check if it's what we want
283 if (interfaces.find(interface::invMotherboard) == interfaces.end())
284 {
285 return;
286 }
287
288 // Done watching for any new inventory interfaces
289 _inventoryIfacesAddedMatch.reset();
290
291 // Watch the motherboard CCIN, using the service from this signal
292 // for the initial property read.
293 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
294 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
295 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
296}
297
Matt Spinler60c4e792020-03-13 13:45:36 -0500298void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500299 std::string& fruPartNumber,
300 std::string& ccin,
301 std::string& serialNumber) const
302{
303 // For now, attempt to get all of the properties directly on the path
304 // passed in. In the future, may need to make use of an algorithm
305 // to figure out which inventory objects actually hold these
306 // interfaces in the case of non FRUs, or possibly another service
307 // will provide this info. Any missing interfaces will result
308 // in exceptions being thrown.
309
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500310 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500311
312 auto properties =
313 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
314
315 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
316 fruPartNumber = std::string{value.begin(), value.end()};
317
318 value = std::get<std::vector<uint8_t>>(properties["CC"]);
319 ccin = std::string{value.begin(), value.end()};
320
321 value = std::get<std::vector<uint8_t>>(properties["SN"]);
322 serialNumber = std::string{value.begin(), value.end()};
323}
324
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500325std::string
326 DataInterface::getLocationCode(const std::string& inventoryPath) const
327{
328 auto service = getService(inventoryPath, interface::locCode);
329
330 DBusValue locCode;
331 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
332 locCode);
333
334 return std::get<std::string>(locCode);
335}
336
Matt Spinler5fb24c12020-06-04 11:21:33 -0500337std::string
338 DataInterface::addLocationCodePrefix(const std::string& locationCode)
339{
340 static const std::string locationCodePrefix{"Ufcs-"};
341
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500342 // Technically there are 2 location code prefixes, Ufcs and Umts, so
343 // if it already starts with a U then don't need to do anything.
344 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500345 {
346 return locationCodePrefix + locationCode;
347 }
348
349 return locationCode;
350}
351
352std::string DataInterface::expandLocationCode(const std::string& locationCode,
353 uint16_t node) const
354{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500355 auto method =
356 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
357 interface::vpdManager, "GetExpandedLocationCode");
358
359 method.append(addLocationCodePrefix(locationCode),
360 static_cast<uint16_t>(0));
361
362 auto reply = _bus.call(method);
363
364 std::string expandedLocationCode;
365 reply.read(expandedLocationCode);
366
367 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500368}
369
Matt Spinler2f9225a2020-08-05 12:58:49 -0500370std::string
371 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
372 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500373{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500374 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
375 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500376
Matt Spinler2f9225a2020-08-05 12:58:49 -0500377 auto method =
378 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
379 interface::vpdManager, methodName.c_str());
380
381 if (expanded)
382 {
383 method.append(locationCode);
384 }
385 else
386 {
387 method.append(addLocationCodePrefix(locationCode), node);
388 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500389
390 auto reply = _bus.call(method);
391
392 std::vector<sdbusplus::message::object_path> entries;
393 reply.read(entries);
394
395 // Get the shortest entry from the paths received, as this
396 // would be the path furthest up the inventory hierarchy so
397 // would be the parent FRU. There is guaranteed to at least
398 // be one entry if the call didn't fail.
399 std::string shortest{entries[0]};
400
401 std::for_each(entries.begin(), entries.end(),
402 [&shortest](const auto& path) {
403 if (path.str.size() < shortest.size())
404 {
405 shortest = path;
406 }
407 });
408
409 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500410}
411
Matt Spinler34a904c2020-08-05 14:53:28 -0500412std::string
413 DataInterface::getFaultLEDGroup(const std::string& inventoryPath) const
414{
415 auto associationPath = inventoryPath + "/" + "fault_led_group";
416 auto service = getService(associationPath, interface::association);
417
418 DBusValue endpoints;
419 getProperty(service, associationPath, interface::association, "endpoints",
420 endpoints);
421 auto paths = std::get<std::vector<std::string>>(endpoints);
422 if (paths.empty())
423 {
424 throw std::runtime_error("Association endpoints property empty");
425 }
426
427 return paths[0];
428}
429
430void DataInterface::assertLEDGroup(const std::string& ledGroup,
431 bool value) const
432{
433 DBusValue variant = value;
434 auto method =
435 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
436 interface::dbusProperty, "Set");
437 method.append(interface::ledGroup, "Asserted", variant);
438 _bus.call(method);
439}
440
Matt Spinler1ab66962020-10-29 13:21:44 -0500441std::vector<std::string> DataInterface::getSystemNames() const
442{
443 DBusSubTree subtree;
444 DBusValue names;
445
446 auto method = _bus.new_method_call(service_name::objectMapper,
447 object_path::objectMapper,
448 interface::objectMapper, "GetSubTree");
449 method.append(std::string{"/"}, 0,
450 std::vector<std::string>{interface::compatible});
451 auto reply = _bus.call(method);
452
453 reply.read(subtree);
454 if (subtree.empty())
455 {
456 throw std::runtime_error("Compatible interface not on D-Bus");
457 }
458
459 const auto& object = *(subtree.begin());
460 const auto& path = object.first;
461 const auto& service = object.second.begin()->first;
462
463 getProperty(service, path, interface::compatible, "Names", names);
464
465 return std::get<std::vector<std::string>>(names);
466}
467
Matt Spinlerc8705e22019-09-11 12:36:07 -0500468} // namespace pels
469} // namespace openpower