blob: 95de4bbf7f4fa28abd0867ce68edbd04a34e9ec2 [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 Spinlerb3d488f2020-02-21 15:30:46 -060037constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060038constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
39constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060040constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060041constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060042constexpr auto enableHostPELs =
43 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerc8705e22019-09-11 12:36:07 -050044} // namespace object_path
45
46namespace interface
47{
48constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
49constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
50constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlera7d9d962019-11-06 15:01:25 -060051constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status";
Matt Spinlerb3f51862019-12-09 13:55:10 -060052constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060053constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060054constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
55constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
56constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060057constexpr auto invMotherboard =
58 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
59constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Matt Spinler60c4e792020-03-13 13:45:36 -050060constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinlerc8705e22019-09-11 12:36:07 -050061} // namespace interface
62
Matt Spinlera7d9d962019-11-06 15:01:25 -060063using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060064using sdbusplus::exception::SdBusError;
Matt Spinlera7d9d962019-11-06 15:01:25 -060065
Matt Spinlerc8705e22019-09-11 12:36:07 -050066DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
67{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060068 readBMCFWVersion();
69 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060070 readBMCFWVersionID();
Matt Spinlerb3d488f2020-02-21 15:30:46 -060071 readMotherboardCCIN();
Matt Spinler2a28c932020-02-03 14:23:40 -060072
73 // Watch both the Model and SN properties on the system's Asset iface
74 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
75 bus, object_path::systemInv, interface::invAsset, *this,
76 [this](const auto& properties) {
77 auto model = properties.find("Model");
78 if (model != properties.end())
79 {
80 this->_machineTypeModel = std::get<std::string>(model->second);
81 }
82
83 auto sn = properties.find("SerialNumber");
84 if (sn != properties.end())
85 {
86 this->_machineSerialNumber = std::get<std::string>(sn->second);
87 }
88 }));
89
90 // Watch the OperatingSystemState property
91 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
92 bus, object_path::hostState, interface::osStatus,
93 "OperatingSystemState", *this, [this](const auto& value) {
94 auto status =
95 Status::convertOSStatusFromString(std::get<std::string>(value));
96
97 if ((status == Status::OSStatus::BootComplete) ||
98 (status == Status::OSStatus::Standby))
99 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600100 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600101 }
102 else
103 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600104 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600105 }
106 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600107
108 // Watch the host PEL enable property
109 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
110 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
111 [this](const auto& value) {
112 this->_sendPELsToHost = std::get<bool>(value);
113 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600114
115 // Watch the BMCState property
116 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
117 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
118 *this, [this](const auto& value) {
119 this->_bmcState = std::get<std::string>(value);
120 }));
121
122 // Watch the chassis current and requested power state properties
123 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
124 bus, object_path::chassisState, interface::chassisState, *this,
125 [this](const auto& properties) {
126 auto state = properties.find("CurrentPowerState");
127 if (state != properties.end())
128 {
129 this->_chassisState = std::get<std::string>(state->second);
130 }
131
132 auto trans = properties.find("RequestedPowerTransition");
133 if (trans != properties.end())
134 {
135 this->_chassisTransition = std::get<std::string>(trans->second);
136 }
137 }));
138
139 // Watch the CurrentHostState property
140 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
141 bus, object_path::hostState, interface::hostState, "CurrentHostState",
142 *this, [this](const auto& value) {
143 this->_hostState = std::get<std::string>(value);
144 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500145}
146
Matt Spinler2a28c932020-02-03 14:23:40 -0600147DBusPropertyMap
148 DataInterface::getAllProperties(const std::string& service,
149 const std::string& objectPath,
150 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500151{
152 DBusPropertyMap properties;
153
154 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
155 interface::dbusProperty, "GetAll");
156 method.append(interface);
157 auto reply = _bus.call(method);
158
159 reply.read(properties);
160
161 return properties;
162}
163
Matt Spinlera7d9d962019-11-06 15:01:25 -0600164void DataInterface::getProperty(const std::string& service,
165 const std::string& objectPath,
166 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600167 const std::string& property,
168 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600169{
170
171 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
172 interface::dbusProperty, "Get");
173 method.append(interface, property);
174 auto reply = _bus.call(method);
175
176 reply.read(value);
177}
178
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600179DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
180{
181
182 auto method = _bus.new_method_call(
183 service_name::objectMapper, object_path::objectMapper,
184 interface::objectMapper, "GetSubTreePaths");
185
186 method.append(std::string{"/"}, 0, interfaces);
187
188 auto reply = _bus.call(method);
189
190 DBusPathList paths;
191 reply.read(paths);
192
193 return paths;
194}
195
Matt Spinlerc8705e22019-09-11 12:36:07 -0500196DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600197 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500198{
199 auto method = _bus.new_method_call(service_name::objectMapper,
200 object_path::objectMapper,
201 interface::objectMapper, "GetObject");
202
203 method.append(objectPath, std::vector<std::string>({interface}));
204
205 auto reply = _bus.call(method);
206
207 std::map<DBusService, DBusInterfaceList> response;
208 reply.read(response);
209
210 if (!response.empty())
211 {
212 return response.begin()->first;
213 }
214
215 return std::string{};
216}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600217
Matt Spinler677381b2020-01-23 10:04:29 -0600218/**
219 * @brief Return a value found in the /etc/os-release file
220 *
221 * @param[in] key - The key name, like "VERSION"
222 *
223 * @return std::optional<std::string> - The value
224 */
225std::optional<std::string> getOSReleaseValue(const std::string& key)
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600226{
227 std::ifstream versionFile{BMC_VERSION_FILE};
228 std::string line;
Matt Spinler677381b2020-01-23 10:04:29 -0600229 std::string keyPattern{key + '='};
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600230
231 while (std::getline(versionFile, line))
232 {
Matt Spinler677381b2020-01-23 10:04:29 -0600233 if (line.find(keyPattern) != std::string::npos)
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600234 {
235 auto pos = line.find_first_of('"') + 1;
Matt Spinler677381b2020-01-23 10:04:29 -0600236 auto value = line.substr(pos, line.find_last_of('"') - pos);
237 return value;
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600238 }
239 }
Matt Spinler677381b2020-01-23 10:04:29 -0600240
241 return std::nullopt;
242}
243
244void DataInterface::readBMCFWVersion()
245{
246 _bmcFWVersion = getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600247}
248
249void DataInterface::readServerFWVersion()
250{
251 // Not available yet
252}
253
Matt Spinler677381b2020-01-23 10:04:29 -0600254void DataInterface::readBMCFWVersionID()
255{
256 _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or("");
257}
258
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600259void DataInterface::readMotherboardCCIN()
260{
261 try
262 {
263 // First, try to find the motherboard
264 auto motherboards = getPaths({interface::invMotherboard});
265 if (motherboards.empty())
266 {
267 throw std::runtime_error("No motherboards yet");
268 }
269
270 // Found it, so now get the CCIN
271 _properties.emplace_back(
272 std::make_unique<PropertyWatcher<DataInterface>>(
273 _bus, motherboards.front(), interface::viniRecordVPD, "CC",
274 *this,
275 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
276 }
277 catch (const std::exception& e)
278 {
279 // No motherboard in the inventory yet - watch for it
280 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
281 _bus, match_rules::interfacesAdded(object_path::baseInv),
282 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this,
283 std::placeholders::_1));
284 }
285}
286
287void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg)
288{
289 sdbusplus::message::object_path path;
290 DBusInterfaceMap interfaces;
291
292 msg.read(path, interfaces);
293
294 // This is watching the whole inventory, so check if it's what we want
295 if (interfaces.find(interface::invMotherboard) == interfaces.end())
296 {
297 return;
298 }
299
300 // Done watching for any new inventory interfaces
301 _inventoryIfacesAddedMatch.reset();
302
303 // Watch the motherboard CCIN, using the service from this signal
304 // for the initial property read.
305 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
306 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this,
307 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); }));
308}
309
Matt Spinler60c4e792020-03-13 13:45:36 -0500310void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
311 std::string& locationCode,
312 std::string& fruPartNumber,
313 std::string& ccin,
314 std::string& serialNumber) const
315{
316 // For now, attempt to get all of the properties directly on the path
317 // passed in. In the future, may need to make use of an algorithm
318 // to figure out which inventory objects actually hold these
319 // interfaces in the case of non FRUs, or possibly another service
320 // will provide this info. Any missing interfaces will result
321 // in exceptions being thrown.
322
323 auto service = getService(inventoryPath, interface::locCode);
324
325 DBusValue locCode;
326 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
327 locCode);
328
329 locationCode = std::get<std::string>(locCode);
330
331 auto properties =
332 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
333
334 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
335 fruPartNumber = std::string{value.begin(), value.end()};
336
337 value = std::get<std::vector<uint8_t>>(properties["CC"]);
338 ccin = std::string{value.begin(), value.end()};
339
340 value = std::get<std::vector<uint8_t>>(properties["SN"]);
341 serialNumber = std::string{value.begin(), value.end()};
342}
343
Matt Spinlerc8705e22019-09-11 12:36:07 -0500344} // namespace pels
345} // namespace openpower