blob: 4cad337a3a4da7d6f81ebca1979ec4656bbffa38 [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 Spinler6ea4d5f2020-05-20 13:31:07 -050067constexpr auto invCompatible =
68 "xyz.openbmc_project.Inventory.Decorator.Compatible";
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 Spinler6ea4d5f2020-05-20 13:31:07 -0500156
157 // Watch the compatible system names property
158 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
159 bus, object_path::chassisInv, interface::invCompatible, "Names", *this,
160 [this](const auto& value) {
161 this->_systemNames = std::get<std::vector<std::string>>(value);
162 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500163}
164
Matt Spinler2a28c932020-02-03 14:23:40 -0600165DBusPropertyMap
166 DataInterface::getAllProperties(const std::string& service,
167 const std::string& objectPath,
168 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500169{
170 DBusPropertyMap properties;
171
172 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
173 interface::dbusProperty, "GetAll");
174 method.append(interface);
175 auto reply = _bus.call(method);
176
177 reply.read(properties);
178
179 return properties;
180}
181
Matt Spinlera7d9d962019-11-06 15:01:25 -0600182void DataInterface::getProperty(const std::string& service,
183 const std::string& objectPath,
184 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600185 const std::string& property,
186 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600187{
188
189 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
190 interface::dbusProperty, "Get");
191 method.append(interface, property);
192 auto reply = _bus.call(method);
193
194 reply.read(value);
195}
196
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600197DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
198{
199
200 auto method = _bus.new_method_call(
201 service_name::objectMapper, object_path::objectMapper,
202 interface::objectMapper, "GetSubTreePaths");
203
204 method.append(std::string{"/"}, 0, interfaces);
205
206 auto reply = _bus.call(method);
207
208 DBusPathList paths;
209 reply.read(paths);
210
211 return paths;
212}
213
Matt Spinlerc8705e22019-09-11 12:36:07 -0500214DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600215 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500216{
217 auto method = _bus.new_method_call(service_name::objectMapper,
218 object_path::objectMapper,
219 interface::objectMapper, "GetObject");
220
221 method.append(objectPath, std::vector<std::string>({interface}));
222
223 auto reply = _bus.call(method);
224
225 std::map<DBusService, DBusInterfaceList> response;
226 reply.read(response);
227
228 if (!response.empty())
229 {
230 return response.begin()->first;
231 }
232
233 return std::string{};
234}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600235
Matt Spinler677381b2020-01-23 10:04:29 -0600236void DataInterface::readBMCFWVersion()
237{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500238 _bmcFWVersion =
239 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600240}
241
242void DataInterface::readServerFWVersion()
243{
244 // Not available yet
245}
246
Matt Spinler677381b2020-01-23 10:04:29 -0600247void DataInterface::readBMCFWVersionID()
248{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500249 _bmcFWVersionID =
250 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600251}
252
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600253void DataInterface::readMotherboardCCIN()
254{
255 try
256 {
257 // First, try to find the motherboard
258 auto motherboards = getPaths({interface::invMotherboard});
259 if (motherboards.empty())
260 {
261 throw std::runtime_error("No motherboards yet");
262 }
263
264 // Found it, so now get the CCIN
265 _properties.emplace_back(
266 std::make_unique<PropertyWatcher<DataInterface>>(
267 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
268 *this,
269 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
270 }
271 catch (const std::exception& e)
272 {
273 // No motherboard in the inventory yet - watch for it
274 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
275 _bus, match_rules::interfacesAdded(object_path::baseInv),
276 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
277 std::placeholders::_1));
278 }
279}
280
281void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
282{
283 sdbusplus::message::object_path path;
284 DBusInterfaceMap interfaces;
285
286 msg.read(path, interfaces);
287
288 // This is watching the whole inventory, so check if it's what we want
289 if (interfaces.find(interface::invMotherboard) == interfaces.end())
290 {
291 return;
292 }
293
294 // Done watching for any new inventory interfaces
295 _inventoryIfacesAddedMatch.reset();
296
297 // Watch the motherboard CCIN, using the service from this signal
298 // for the initial property read.
299 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
300 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
301 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
302}
303
Matt Spinler60c4e792020-03-13 13:45:36 -0500304void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500305 std::string& fruPartNumber,
306 std::string& ccin,
307 std::string& serialNumber) const
308{
309 // For now, attempt to get all of the properties directly on the path
310 // passed in. In the future, may need to make use of an algorithm
311 // to figure out which inventory objects actually hold these
312 // interfaces in the case of non FRUs, or possibly another service
313 // will provide this info. Any missing interfaces will result
314 // in exceptions being thrown.
315
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500316 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500317
318 auto properties =
319 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
320
321 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
322 fruPartNumber = std::string{value.begin(), value.end()};
323
324 value = std::get<std::vector<uint8_t>>(properties["CC"]);
325 ccin = std::string{value.begin(), value.end()};
326
327 value = std::get<std::vector<uint8_t>>(properties["SN"]);
328 serialNumber = std::string{value.begin(), value.end()};
329}
330
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500331std::string
332 DataInterface::getLocationCode(const std::string& inventoryPath) const
333{
334 auto service = getService(inventoryPath, interface::locCode);
335
336 DBusValue locCode;
337 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
338 locCode);
339
340 return std::get<std::string>(locCode);
341}
342
Matt Spinler5fb24c12020-06-04 11:21:33 -0500343std::string
344 DataInterface::addLocationCodePrefix(const std::string& locationCode)
345{
346 static const std::string locationCodePrefix{"Ufcs-"};
347
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500348 // Technically there are 2 location code prefixes, Ufcs and Umts, so
349 // if it already starts with a U then don't need to do anything.
350 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500351 {
352 return locationCodePrefix + locationCode;
353 }
354
355 return locationCode;
356}
357
358std::string DataInterface::expandLocationCode(const std::string& locationCode,
359 uint16_t node) const
360{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500361 auto method =
362 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
363 interface::vpdManager, "GetExpandedLocationCode");
364
365 method.append(addLocationCodePrefix(locationCode),
366 static_cast<uint16_t>(0));
367
368 auto reply = _bus.call(method);
369
370 std::string expandedLocationCode;
371 reply.read(expandedLocationCode);
372
373 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500374}
375
Matt Spinler2f9225a2020-08-05 12:58:49 -0500376std::string
377 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
378 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500379{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500380 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
381 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500382
Matt Spinler2f9225a2020-08-05 12:58:49 -0500383 auto method =
384 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
385 interface::vpdManager, methodName.c_str());
386
387 if (expanded)
388 {
389 method.append(locationCode);
390 }
391 else
392 {
393 method.append(addLocationCodePrefix(locationCode), node);
394 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500395
396 auto reply = _bus.call(method);
397
398 std::vector<sdbusplus::message::object_path> entries;
399 reply.read(entries);
400
401 // Get the shortest entry from the paths received, as this
402 // would be the path furthest up the inventory hierarchy so
403 // would be the parent FRU. There is guaranteed to at least
404 // be one entry if the call didn't fail.
405 std::string shortest{entries[0]};
406
407 std::for_each(entries.begin(), entries.end(),
408 [&shortest](const auto& path) {
409 if (path.str.size() < shortest.size())
410 {
411 shortest = path;
412 }
413 });
414
415 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500416}
417
Matt Spinler34a904c2020-08-05 14:53:28 -0500418std::string
419 DataInterface::getFaultLEDGroup(const std::string& inventoryPath) const
420{
421 auto associationPath = inventoryPath + "/" + "fault_led_group";
422 auto service = getService(associationPath, interface::association);
423
424 DBusValue endpoints;
425 getProperty(service, associationPath, interface::association, "endpoints",
426 endpoints);
427 auto paths = std::get<std::vector<std::string>>(endpoints);
428 if (paths.empty())
429 {
430 throw std::runtime_error("Association endpoints property empty");
431 }
432
433 return paths[0];
434}
435
436void DataInterface::assertLEDGroup(const std::string& ledGroup,
437 bool value) const
438{
439 DBusValue variant = value;
440 auto method =
441 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
442 interface::dbusProperty, "Set");
443 method.append(interface::ledGroup, "Asserted", variant);
444 _bus.call(method);
445}
446
Matt Spinlerc8705e22019-09-11 12:36:07 -0500447} // namespace pels
448} // namespace openpower