blob: 14778060782a479c9b00e8023238889859946556 [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 Spinlerc8705e22019-09-11 12:36:07 -050034} // namespace service_name
35
36namespace object_path
37{
38constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
39constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050040constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060041constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060042constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
43constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060044constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060045constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060046constexpr auto enableHostPELs =
47 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050048constexpr auto vpdManager = "/com/ibm/VPD/Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050049} // namespace object_path
50
51namespace interface
52{
53constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
54constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
55constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlera7d9d962019-11-06 15:01:25 -060056constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status";
Matt Spinlerb3f51862019-12-09 13:55:10 -060057constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060058constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060059constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
60constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
61constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060062constexpr auto invMotherboard =
63 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
64constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Matt Spinler60c4e792020-03-13 13:45:36 -050065constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050066constexpr auto invCompatible =
67 "xyz.openbmc_project.Inventory.Decorator.Compatible";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050068constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050069} // namespace interface
70
Matt Spinlera7d9d962019-11-06 15:01:25 -060071using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060072using sdbusplus::exception::SdBusError;
Matt Spinlera7d9d962019-11-06 15:01:25 -060073
Matt Spinlerc8705e22019-09-11 12:36:07 -050074DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
75{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060076 readBMCFWVersion();
77 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060078 readBMCFWVersionID();
Matt Spinlerb3d488f2020-02-21 15:30:46 -060079 readMotherboardCCIN();
Matt Spinler2a28c932020-02-03 14:23:40 -060080
81 // Watch both the Model and SN properties on the system's Asset iface
82 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
83 bus, object_path::systemInv, interface::invAsset, *this,
84 [this](const auto& properties) {
85 auto model = properties.find("Model");
86 if (model != properties.end())
87 {
88 this->_machineTypeModel = std::get<std::string>(model->second);
89 }
90
91 auto sn = properties.find("SerialNumber");
92 if (sn != properties.end())
93 {
94 this->_machineSerialNumber = std::get<std::string>(sn->second);
95 }
96 }));
97
98 // Watch the OperatingSystemState property
99 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
100 bus, object_path::hostState, interface::osStatus,
101 "OperatingSystemState", *this, [this](const auto& value) {
102 auto status =
103 Status::convertOSStatusFromString(std::get<std::string>(value));
104
105 if ((status == Status::OSStatus::BootComplete) ||
106 (status == Status::OSStatus::Standby))
107 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600108 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600109 }
110 else
111 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600112 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600113 }
114 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600115
116 // Watch the host PEL enable property
117 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
118 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
119 [this](const auto& value) {
120 this->_sendPELsToHost = std::get<bool>(value);
121 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600122
123 // Watch the BMCState property
124 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
125 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
126 *this, [this](const auto& value) {
127 this->_bmcState = std::get<std::string>(value);
128 }));
129
130 // Watch the chassis current and requested power state properties
131 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
132 bus, object_path::chassisState, interface::chassisState, *this,
133 [this](const auto& properties) {
134 auto state = properties.find("CurrentPowerState");
135 if (state != properties.end())
136 {
137 this->_chassisState = std::get<std::string>(state->second);
138 }
139
140 auto trans = properties.find("RequestedPowerTransition");
141 if (trans != properties.end())
142 {
143 this->_chassisTransition = std::get<std::string>(trans->second);
144 }
145 }));
146
147 // Watch the CurrentHostState property
148 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
149 bus, object_path::hostState, interface::hostState, "CurrentHostState",
150 *this, [this](const auto& value) {
151 this->_hostState = std::get<std::string>(value);
152 }));
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500153
154 // Watch the compatible system names property
155 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
156 bus, object_path::chassisInv, interface::invCompatible, "Names", *this,
157 [this](const auto& value) {
158 this->_systemNames = std::get<std::vector<std::string>>(value);
159 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500160}
161
Matt Spinler2a28c932020-02-03 14:23:40 -0600162DBusPropertyMap
163 DataInterface::getAllProperties(const std::string& service,
164 const std::string& objectPath,
165 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500166{
167 DBusPropertyMap properties;
168
169 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
170 interface::dbusProperty, "GetAll");
171 method.append(interface);
172 auto reply = _bus.call(method);
173
174 reply.read(properties);
175
176 return properties;
177}
178
Matt Spinlera7d9d962019-11-06 15:01:25 -0600179void DataInterface::getProperty(const std::string& service,
180 const std::string& objectPath,
181 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600182 const std::string& property,
183 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600184{
185
186 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
187 interface::dbusProperty, "Get");
188 method.append(interface, property);
189 auto reply = _bus.call(method);
190
191 reply.read(value);
192}
193
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600194DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
195{
196
197 auto method = _bus.new_method_call(
198 service_name::objectMapper, object_path::objectMapper,
199 interface::objectMapper, "GetSubTreePaths");
200
201 method.append(std::string{"/"}, 0, interfaces);
202
203 auto reply = _bus.call(method);
204
205 DBusPathList paths;
206 reply.read(paths);
207
208 return paths;
209}
210
Matt Spinlerc8705e22019-09-11 12:36:07 -0500211DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600212 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500213{
214 auto method = _bus.new_method_call(service_name::objectMapper,
215 object_path::objectMapper,
216 interface::objectMapper, "GetObject");
217
218 method.append(objectPath, std::vector<std::string>({interface}));
219
220 auto reply = _bus.call(method);
221
222 std::map<DBusService, DBusInterfaceList> response;
223 reply.read(response);
224
225 if (!response.empty())
226 {
227 return response.begin()->first;
228 }
229
230 return std::string{};
231}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600232
Matt Spinler677381b2020-01-23 10:04:29 -0600233void DataInterface::readBMCFWVersion()
234{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500235 _bmcFWVersion =
236 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600237}
238
239void DataInterface::readServerFWVersion()
240{
241 // Not available yet
242}
243
Matt Spinler677381b2020-01-23 10:04:29 -0600244void DataInterface::readBMCFWVersionID()
245{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500246 _bmcFWVersionID =
247 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600248}
249
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600250void DataInterface::readMotherboardCCIN()
251{
252 try
253 {
254 // First, try to find the motherboard
255 auto motherboards = getPaths({interface::invMotherboard});
256 if (motherboards.empty())
257 {
258 throw std::runtime_error("No motherboards yet");
259 }
260
261 // Found it, so now get the CCIN
262 _properties.emplace_back(
263 std::make_unique<PropertyWatcher<DataInterface>>(
264 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
265 *this,
266 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
267 }
268 catch (const std::exception& e)
269 {
270 // No motherboard in the inventory yet - watch for it
271 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
272 _bus, match_rules::interfacesAdded(object_path::baseInv),
273 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
274 std::placeholders::_1));
275 }
276}
277
278void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
279{
280 sdbusplus::message::object_path path;
281 DBusInterfaceMap interfaces;
282
283 msg.read(path, interfaces);
284
285 // This is watching the whole inventory, so check if it's what we want
286 if (interfaces.find(interface::invMotherboard) == interfaces.end())
287 {
288 return;
289 }
290
291 // Done watching for any new inventory interfaces
292 _inventoryIfacesAddedMatch.reset();
293
294 // Watch the motherboard CCIN, using the service from this signal
295 // for the initial property read.
296 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
297 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
298 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
299}
300
Matt Spinler60c4e792020-03-13 13:45:36 -0500301void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500302 std::string& fruPartNumber,
303 std::string& ccin,
304 std::string& serialNumber) const
305{
306 // For now, attempt to get all of the properties directly on the path
307 // passed in. In the future, may need to make use of an algorithm
308 // to figure out which inventory objects actually hold these
309 // interfaces in the case of non FRUs, or possibly another service
310 // will provide this info. Any missing interfaces will result
311 // in exceptions being thrown.
312
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500313 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500314
315 auto properties =
316 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
317
318 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
319 fruPartNumber = std::string{value.begin(), value.end()};
320
321 value = std::get<std::vector<uint8_t>>(properties["CC"]);
322 ccin = std::string{value.begin(), value.end()};
323
324 value = std::get<std::vector<uint8_t>>(properties["SN"]);
325 serialNumber = std::string{value.begin(), value.end()};
326}
327
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500328std::string
329 DataInterface::getLocationCode(const std::string& inventoryPath) const
330{
331 auto service = getService(inventoryPath, interface::locCode);
332
333 DBusValue locCode;
334 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
335 locCode);
336
337 return std::get<std::string>(locCode);
338}
339
Matt Spinler5fb24c12020-06-04 11:21:33 -0500340std::string
341 DataInterface::addLocationCodePrefix(const std::string& locationCode)
342{
343 static const std::string locationCodePrefix{"Ufcs-"};
344
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500345 // Technically there are 2 location code prefixes, Ufcs and Umts, so
346 // if it already starts with a U then don't need to do anything.
347 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500348 {
349 return locationCodePrefix + locationCode;
350 }
351
352 return locationCode;
353}
354
355std::string DataInterface::expandLocationCode(const std::string& locationCode,
356 uint16_t node) const
357{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500358 auto method =
359 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
360 interface::vpdManager, "GetExpandedLocationCode");
361
362 method.append(addLocationCodePrefix(locationCode),
363 static_cast<uint16_t>(0));
364
365 auto reply = _bus.call(method);
366
367 std::string expandedLocationCode;
368 reply.read(expandedLocationCode);
369
370 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500371}
372
373std::string DataInterface::getInventoryFromLocCode(
374 const std::string& unexpandedLocationCode, uint16_t node) const
375{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500376 auto method = _bus.new_method_call(
377 service_name::vpdManager, object_path::vpdManager,
378 interface::vpdManager, "GetFRUsByUnexpandedLocationCode");
379
380 method.append(addLocationCodePrefix(unexpandedLocationCode),
381 static_cast<uint16_t>(0));
382
383 auto reply = _bus.call(method);
384
385 std::vector<sdbusplus::message::object_path> entries;
386 reply.read(entries);
387
388 // Get the shortest entry from the paths received, as this
389 // would be the path furthest up the inventory hierarchy so
390 // would be the parent FRU. There is guaranteed to at least
391 // be one entry if the call didn't fail.
392 std::string shortest{entries[0]};
393
394 std::for_each(entries.begin(), entries.end(),
395 [&shortest](const auto& path) {
396 if (path.str.size() < shortest.size())
397 {
398 shortest = path;
399 }
400 });
401
402 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500403}
404
Matt Spinlerc8705e22019-09-11 12:36:07 -0500405} // namespace pels
406} // namespace openpower