blob: db807575c174377291a01fb9bfe59a52893d367e [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 Spinlercad9c2b2019-12-02 15:42:01 -060020#include <fstream>
Matt Spinlera7d9d962019-11-06 15:01:25 -060021#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
22
Matt Spinlerc8705e22019-09-11 12:36:07 -050023namespace openpower
24{
25namespace pels
26{
27
28namespace service_name
29{
30constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
31} // namespace service_name
32
33namespace object_path
34{
35constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
36constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050037constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060038constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060039constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
40constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060041constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060042constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060043constexpr auto enableHostPELs =
44 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerc8705e22019-09-11 12:36:07 -050045} // namespace object_path
46
47namespace interface
48{
49constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
50constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
51constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlera7d9d962019-11-06 15:01:25 -060052constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status";
Matt Spinlerb3f51862019-12-09 13:55:10 -060053constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060054constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060055constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
56constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
57constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060058constexpr auto invMotherboard =
59 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
60constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Matt Spinler60c4e792020-03-13 13:45:36 -050061constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050062constexpr auto invCompatible =
63 "xyz.openbmc_project.Inventory.Decorator.Compatible";
Matt Spinlerc8705e22019-09-11 12:36:07 -050064} // namespace interface
65
Matt Spinlera7d9d962019-11-06 15:01:25 -060066using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060067using sdbusplus::exception::SdBusError;
Matt Spinlera7d9d962019-11-06 15:01:25 -060068
Matt Spinlerc8705e22019-09-11 12:36:07 -050069DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
70{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060071 readBMCFWVersion();
72 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060073 readBMCFWVersionID();
Matt Spinlerb3d488f2020-02-21 15:30:46 -060074 readMotherboardCCIN();
Matt Spinler2a28c932020-02-03 14:23:40 -060075
76 // Watch both the Model and SN properties on the system's Asset iface
77 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
78 bus, object_path::systemInv, interface::invAsset, *this,
79 [this](const auto& properties) {
80 auto model = properties.find("Model");
81 if (model != properties.end())
82 {
83 this->_machineTypeModel = std::get<std::string>(model->second);
84 }
85
86 auto sn = properties.find("SerialNumber");
87 if (sn != properties.end())
88 {
89 this->_machineSerialNumber = std::get<std::string>(sn->second);
90 }
91 }));
92
93 // Watch the OperatingSystemState property
94 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
95 bus, object_path::hostState, interface::osStatus,
96 "OperatingSystemState", *this, [this](const auto& value) {
97 auto status =
98 Status::convertOSStatusFromString(std::get<std::string>(value));
99
100 if ((status == Status::OSStatus::BootComplete) ||
101 (status == Status::OSStatus::Standby))
102 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600103 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600104 }
105 else
106 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600107 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600108 }
109 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600110
111 // Watch the host PEL enable property
112 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
113 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
114 [this](const auto& value) {
115 this->_sendPELsToHost = std::get<bool>(value);
116 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600117
118 // Watch the BMCState property
119 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
120 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
121 *this, [this](const auto& value) {
122 this->_bmcState = std::get<std::string>(value);
123 }));
124
125 // Watch the chassis current and requested power state properties
126 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
127 bus, object_path::chassisState, interface::chassisState, *this,
128 [this](const auto& properties) {
129 auto state = properties.find("CurrentPowerState");
130 if (state != properties.end())
131 {
132 this->_chassisState = std::get<std::string>(state->second);
133 }
134
135 auto trans = properties.find("RequestedPowerTransition");
136 if (trans != properties.end())
137 {
138 this->_chassisTransition = std::get<std::string>(trans->second);
139 }
140 }));
141
142 // Watch the CurrentHostState property
143 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
144 bus, object_path::hostState, interface::hostState, "CurrentHostState",
145 *this, [this](const auto& value) {
146 this->_hostState = std::get<std::string>(value);
147 }));
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500148
149 // Watch the compatible system names property
150 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
151 bus, object_path::chassisInv, interface::invCompatible, "Names", *this,
152 [this](const auto& value) {
153 this->_systemNames = std::get<std::vector<std::string>>(value);
154 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500155}
156
Matt Spinler2a28c932020-02-03 14:23:40 -0600157DBusPropertyMap
158 DataInterface::getAllProperties(const std::string& service,
159 const std::string& objectPath,
160 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500161{
162 DBusPropertyMap properties;
163
164 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
165 interface::dbusProperty, "GetAll");
166 method.append(interface);
167 auto reply = _bus.call(method);
168
169 reply.read(properties);
170
171 return properties;
172}
173
Matt Spinlera7d9d962019-11-06 15:01:25 -0600174void DataInterface::getProperty(const std::string& service,
175 const std::string& objectPath,
176 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600177 const std::string& property,
178 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600179{
180
181 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
182 interface::dbusProperty, "Get");
183 method.append(interface, property);
184 auto reply = _bus.call(method);
185
186 reply.read(value);
187}
188
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600189DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
190{
191
192 auto method = _bus.new_method_call(
193 service_name::objectMapper, object_path::objectMapper,
194 interface::objectMapper, "GetSubTreePaths");
195
196 method.append(std::string{"/"}, 0, interfaces);
197
198 auto reply = _bus.call(method);
199
200 DBusPathList paths;
201 reply.read(paths);
202
203 return paths;
204}
205
Matt Spinlerc8705e22019-09-11 12:36:07 -0500206DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600207 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500208{
209 auto method = _bus.new_method_call(service_name::objectMapper,
210 object_path::objectMapper,
211 interface::objectMapper, "GetObject");
212
213 method.append(objectPath, std::vector<std::string>({interface}));
214
215 auto reply = _bus.call(method);
216
217 std::map<DBusService, DBusInterfaceList> response;
218 reply.read(response);
219
220 if (!response.empty())
221 {
222 return response.begin()->first;
223 }
224
225 return std::string{};
226}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600227
Matt Spinler677381b2020-01-23 10:04:29 -0600228/**
229 * @brief Return a value found in the /etc/os-release file
230 *
231 * @param[in] key - The key name, like "VERSION"
232 *
233 * @return std::optional<std::string> - The value
234 */
235std::optional<std::string> getOSReleaseValue(const std::string& key)
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600236{
237 std::ifstream versionFile{BMC_VERSION_FILE};
238 std::string line;
Matt Spinler677381b2020-01-23 10:04:29 -0600239 std::string keyPattern{key + '='};
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600240
241 while (std::getline(versionFile, line))
242 {
Matt Spinler677381b2020-01-23 10:04:29 -0600243 if (line.find(keyPattern) != std::string::npos)
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600244 {
245 auto pos = line.find_first_of('"') + 1;
Matt Spinler677381b2020-01-23 10:04:29 -0600246 auto value = line.substr(pos, line.find_last_of('"') - pos);
247 return value;
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600248 }
249 }
Matt Spinler677381b2020-01-23 10:04:29 -0600250
251 return std::nullopt;
252}
253
254void DataInterface::readBMCFWVersion()
255{
256 _bmcFWVersion = getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600257}
258
259void DataInterface::readServerFWVersion()
260{
261 // Not available yet
262}
263
Matt Spinler677381b2020-01-23 10:04:29 -0600264void DataInterface::readBMCFWVersionID()
265{
266 _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or("");
267}
268
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600269void DataInterface::readMotherboardCCIN()
270{
271 try
272 {
273 // First, try to find the motherboard
274 auto motherboards = getPaths({interface::invMotherboard});
275 if (motherboards.empty())
276 {
277 throw std::runtime_error("No motherboards yet");
278 }
279
280 // Found it, so now get the CCIN
281 _properties.emplace_back(
282 std::make_unique<PropertyWatcher<DataInterface>>(
283 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
284 *this,
285 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
286 }
287 catch (const std::exception& e)
288 {
289 // No motherboard in the inventory yet - watch for it
290 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
291 _bus, match_rules::interfacesAdded(object_path::baseInv),
292 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
293 std::placeholders::_1));
294 }
295}
296
297void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
298{
299 sdbusplus::message::object_path path;
300 DBusInterfaceMap interfaces;
301
302 msg.read(path, interfaces);
303
304 // This is watching the whole inventory, so check if it's what we want
305 if (interfaces.find(interface::invMotherboard) == interfaces.end())
306 {
307 return;
308 }
309
310 // Done watching for any new inventory interfaces
311 _inventoryIfacesAddedMatch.reset();
312
313 // Watch the motherboard CCIN, using the service from this signal
314 // for the initial property read.
315 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
316 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
317 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
318}
319
Matt Spinler60c4e792020-03-13 13:45:36 -0500320void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500321 std::string& fruPartNumber,
322 std::string& ccin,
323 std::string& serialNumber) const
324{
325 // For now, attempt to get all of the properties directly on the path
326 // passed in. In the future, may need to make use of an algorithm
327 // to figure out which inventory objects actually hold these
328 // interfaces in the case of non FRUs, or possibly another service
329 // will provide this info. Any missing interfaces will result
330 // in exceptions being thrown.
331
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500332 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500333
334 auto properties =
335 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
336
337 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
338 fruPartNumber = std::string{value.begin(), value.end()};
339
340 value = std::get<std::vector<uint8_t>>(properties["CC"]);
341 ccin = std::string{value.begin(), value.end()};
342
343 value = std::get<std::vector<uint8_t>>(properties["SN"]);
344 serialNumber = std::string{value.begin(), value.end()};
345}
346
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500347std::string
348 DataInterface::getLocationCode(const std::string& inventoryPath) const
349{
350 auto service = getService(inventoryPath, interface::locCode);
351
352 DBusValue locCode;
353 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
354 locCode);
355
356 return std::get<std::string>(locCode);
357}
358
Matt Spinler5fb24c12020-06-04 11:21:33 -0500359std::string
360 DataInterface::addLocationCodePrefix(const std::string& locationCode)
361{
362 static const std::string locationCodePrefix{"Ufcs-"};
363
364 if (locationCode.find(locationCodePrefix) == std::string::npos)
365 {
366 return locationCodePrefix + locationCode;
367 }
368
369 return locationCode;
370}
371
372std::string DataInterface::expandLocationCode(const std::string& locationCode,
373 uint16_t node) const
374{
375 // TODO: fill in when that API is available
376 return addLocationCodePrefix(locationCode);
377}
378
379std::string DataInterface::getInventoryFromLocCode(
380 const std::string& unexpandedLocationCode, uint16_t node) const
381{
382 // TODO: fill in when that API is available and call on
383 // addLocationCodePrefix(unexpandedLocationCode);
384 return {};
385}
386
Matt Spinlerc8705e22019-09-11 12:36:07 -0500387} // namespace pels
388} // namespace openpower