blob: 2922656842e227bce30848304d3fa4dc8f800c70 [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
Matt Spinlerc8705e22019-09-11 12:36:07 -050017#include "data_interface.hpp"
18
Matt Spinlerf61f2922020-06-23 11:32:49 -050019#include "util.hpp"
20
Matt Spinlera167a7d2023-06-30 15:14:25 -050021#include <phosphor-logging/lg2.hpp>
Matt Spinler5b423652023-05-04 13:08:44 -050022#include <xyz/openbmc_project/State/BMC/server.hpp>
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
Arya K Padmanafba3162024-08-30 07:41:32 -050025#include <filesystem>
26
Arya K Padmand8ae6182024-07-19 06:25:10 -050027#ifdef PEL_ENABLE_PHAL
Arya K Padmanafba3162024-08-30 07:41:32 -050028#include <libekb.H>
29#include <libpdbg.h>
Arya K Padmand8ae6182024-07-19 06:25:10 -050030#include <libphal.H>
31#endif
32
Matt Spinler35a405b2022-03-02 11:42:42 -060033// Use a timeout of 10s for D-Bus calls so if there are
34// timeouts the callers of the PEL creation method won't
35// also timeout.
36constexpr auto dbusTimeout = 10000000;
37
Matt Spinlerc8705e22019-09-11 12:36:07 -050038namespace openpower
39{
40namespace pels
41{
42
43namespace service_name
44{
45constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050046constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050047constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -050048constexpr auto hwIsolation = "org.open_power.HardwareIsolation";
Matt Spinler744d8512022-06-08 08:25:47 -050049constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager";
Vijay Lobo875b6c72021-10-20 17:38:56 -050050constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
Matt Spinler52ee3a42023-07-27 14:54:48 -050051constexpr auto pldm = "xyz.openbmc_project.PLDM";
Matt Spinler412f50e2023-11-14 12:49:52 -060052constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerbbc11ef2024-01-11 16:26:51 -060053constexpr auto entityManager = "xyz.openbmc_project.EntityManager";
Arya K Padmanafba3162024-08-30 07:41:32 -050054constexpr auto systemd = "org.freedesktop.systemd1";
Matt Spinlerc8705e22019-09-11 12:36:07 -050055} // namespace service_name
56
57namespace object_path
58{
59constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
60constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Vijay Lobo81b4dca2021-04-29 00:04:00 -050061constexpr auto motherBoardInv =
62 "/xyz/openbmc_project/inventory/system/chassis/motherboard";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060063constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060064constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
65constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060066constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060067constexpr auto enableHostPELs =
68 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050069constexpr auto vpdManager = "/com/ibm/VPD/Manager";
Sumit Kumar3b8ed7f2021-05-18 12:38:35 -050070constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -050071constexpr auto hwIsolation = "/xyz/openbmc_project/hardware_isolation";
Matt Spinler744d8512022-06-08 08:25:47 -050072constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager";
Vijay Lobo875b6c72021-10-20 17:38:56 -050073constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0";
Arya K Padmanafba3162024-08-30 07:41:32 -050074constexpr auto systemd = "/org/freedesktop/systemd1";
Matt Spinlerc8705e22019-09-11 12:36:07 -050075} // namespace object_path
76
77namespace interface
78{
79constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
80constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
81constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlerf10068d2020-12-02 10:44:08 -060082constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060083constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060084constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
85constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
86constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060087constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Ben Tynere32b7e72021-05-18 12:38:40 -050088constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
Matt Spinlercc8b1112021-12-15 09:30:35 -060089constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode";
Matt Spinler1ab66962020-10-29 13:21:44 -050090constexpr auto compatible =
Matt Spinlerbbc11ef2024-01-11 16:26:51 -060091 "xyz.openbmc_project.Inventory.Decorator.Compatible";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050092constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050093constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
Matt Spinler993168d2021-04-07 16:05:03 -050094constexpr auto operationalStatus =
95 "xyz.openbmc_project.State.Decorator.OperationalStatus";
Sumit Kumar3b8ed7f2021-05-18 12:38:35 -050096constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
Sumit Kumar027bf282022-01-24 11:25:19 -060097constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions";
Sumit Kumar9d43a722021-08-24 09:46:19 -050098constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
99constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress";
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -0500100constexpr auto hwIsolationCreate = "org.open_power.HardwareIsolation.Create";
Sumit Kumar027bf282022-01-24 11:25:19 -0600101constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry";
102constexpr auto association = "xyz.openbmc_project.Association";
Matt Spinler744d8512022-06-08 08:25:47 -0500103constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager";
Vijay Lobo875b6c72021-10-20 17:38:56 -0500104constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
Matt Spinler5b423652023-05-04 13:08:44 -0500105constexpr auto invItem = "xyz.openbmc_project.Inventory.Item";
106constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan";
107constexpr auto invPowerSupply =
108 "xyz.openbmc_project.Inventory.Item.PowerSupply";
Matt Spinler412f50e2023-11-14 12:49:52 -0600109constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
Arya K Padmanafba3162024-08-30 07:41:32 -0500110constexpr auto systemdMgr = "org.freedesktop.systemd1.Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -0500111} // namespace interface
112
Willy Tu6ddbf692023-09-05 10:54:16 -0700113using namespace sdbusplus::server::xyz::openbmc_project::state::boot;
114using namespace sdbusplus::server::xyz::openbmc_project::state;
Matt Spinler5b423652023-05-04 13:08:44 -0500115namespace match_rules = sdbusplus::bus::match::rules;
116
117const DBusInterfaceList hotplugInterfaces{interface::invFan,
118 interface::invPowerSupply};
Arya K Padmanafba3162024-08-30 07:41:32 -0500119static constexpr auto PDBG_DTB_PATH =
120 "/var/lib/phosphor-software-manager/hostfw/running/DEVTREE";
Matt Spinlera7d9d962019-11-06 15:01:25 -0600121
Matt Spinler0d92b522021-06-16 13:28:17 -0600122std::pair<std::string, std::string>
123 DataInterfaceBase::extractConnectorFromLocCode(
124 const std::string& locationCode)
125{
126 auto base = locationCode;
127 std::string connector{};
128
129 auto pos = base.find("-T");
130 if (pos != std::string::npos)
131 {
132 connector = base.substr(pos);
133 base = base.substr(0, pos);
134 }
135
136 return {base, connector};
137}
138
Arya K Padmanafba3162024-08-30 07:41:32 -0500139DataInterface::DataInterface(sdbusplus::bus_t& bus) :
140 _bus(bus), _systemdSlot(nullptr)
Matt Spinlerc8705e22019-09-11 12:36:07 -0500141{
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600142 readBMCFWVersion();
143 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -0600144 readBMCFWVersionID();
Matt Spinler2a28c932020-02-03 14:23:40 -0600145
Matt Spinlerf10068d2020-12-02 10:44:08 -0600146 // Watch the BootProgress property
Matt Spinler2a28c932020-02-03 14:23:40 -0600147 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
Matt Spinlerf10068d2020-12-02 10:44:08 -0600148 bus, object_path::hostState, interface::bootProgress, "BootProgress",
149 *this, [this](const auto& value) {
Patrick Williams075c7922024-08-16 15:19:49 -0400150 this->_bootState = std::get<std::string>(value);
151 auto status = Progress::convertProgressStagesFromString(
152 std::get<std::string>(value));
Matt Spinler2a28c932020-02-03 14:23:40 -0600153
Patrick Williams075c7922024-08-16 15:19:49 -0400154 if ((status == Progress::ProgressStages::SystemInitComplete) ||
155 (status == Progress::ProgressStages::OSRunning))
156 {
157 setHostUp(true);
158 }
159 else
160 {
161 setHostUp(false);
162 }
163 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600164
165 // Watch the host PEL enable property
166 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
167 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
168 [this](const auto& value) {
Patrick Williams075c7922024-08-16 15:19:49 -0400169 if (std::get<bool>(value) != this->_sendPELsToHost)
170 {
171 lg2::info("The send PELs to host setting changed to {VAL}",
172 "VAL", std::get<bool>(value));
173 }
174 this->_sendPELsToHost = std::get<bool>(value);
175 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600176
177 // Watch the BMCState property
178 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
179 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
180 *this, [this](const auto& value) {
Patrick Williams075c7922024-08-16 15:19:49 -0400181 const auto& state = std::get<std::string>(value);
182 this->_bmcState = state;
Matt Spinler5b423652023-05-04 13:08:44 -0500183
Patrick Williams075c7922024-08-16 15:19:49 -0400184 // Wait for BMC ready to start watching for
185 // plugs so things calm down first.
186 if (BMC::convertBMCStateFromString(state) == BMC::BMCState::Ready)
187 {
188 startFruPlugWatch();
189 }
190 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600191
192 // Watch the chassis current and requested power state properties
193 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
194 bus, object_path::chassisState, interface::chassisState, *this,
195 [this](const auto& properties) {
Patrick Williams075c7922024-08-16 15:19:49 -0400196 auto state = properties.find("CurrentPowerState");
197 if (state != properties.end())
198 {
199 this->_chassisState = std::get<std::string>(state->second);
200 }
Matt Spinler4aa23a12020-02-03 15:05:09 -0600201
Patrick Williams075c7922024-08-16 15:19:49 -0400202 auto trans = properties.find("RequestedPowerTransition");
203 if (trans != properties.end())
204 {
205 this->_chassisTransition = std::get<std::string>(trans->second);
206 }
207 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600208
209 // Watch the CurrentHostState property
210 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
211 bus, object_path::hostState, interface::hostState, "CurrentHostState",
212 *this, [this](const auto& value) {
Patrick Williams075c7922024-08-16 15:19:49 -0400213 this->_hostState = std::get<std::string>(value);
214 }));
Matt Spinler744d8512022-06-08 08:25:47 -0500215
216 // Watch the BaseBIOSTable property for the hmc managed attribute
217 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
218 bus, object_path::biosConfigMgr, interface::biosConfigMgr,
219 "BaseBIOSTable", service_name::biosConfigMgr, *this,
220 [this](const auto& value) {
Patrick Williams075c7922024-08-16 15:19:49 -0400221 const auto& attributes = std::get<BiosAttributes>(value);
Matt Spinler744d8512022-06-08 08:25:47 -0500222
Patrick Williams075c7922024-08-16 15:19:49 -0400223 auto it = attributes.find("pvm_hmc_managed");
224 if (it != attributes.end())
Matt Spinler744d8512022-06-08 08:25:47 -0500225 {
Patrick Williams075c7922024-08-16 15:19:49 -0400226 const auto& currentValVariant = std::get<5>(it->second);
227 auto currentVal = std::get_if<std::string>(&currentValVariant);
228 if (currentVal)
229 {
230 this->_hmcManaged =
231 (*currentVal == "Enabled") ? true : false;
232 }
Matt Spinler744d8512022-06-08 08:25:47 -0500233 }
Patrick Williams075c7922024-08-16 15:19:49 -0400234 }));
Arya K Padmanafba3162024-08-30 07:41:32 -0500235
236#ifdef PEL_ENABLE_PHAL
237 if (isPHALDevTreeExist())
238 {
239 initPHAL();
240 }
241 else
242 {
243 // Watch the "openpower-update-bios-attr-table" service to init
244 // PHAL libraries
245 subscribeToSystemdSignals();
246 }
247#endif // PEL_ENABLE_PHAL
Matt Spinlerc8705e22019-09-11 12:36:07 -0500248}
249
Patrick Williams075c7922024-08-16 15:19:49 -0400250DBusPropertyMap DataInterface::getAllProperties(
251 const std::string& service, const std::string& objectPath,
252 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500253{
254 DBusPropertyMap properties;
255
256 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
257 interface::dbusProperty, "GetAll");
258 method.append(interface);
Matt Spinler35a405b2022-03-02 11:42:42 -0600259 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlerc8705e22019-09-11 12:36:07 -0500260
261 reply.read(properties);
262
263 return properties;
264}
265
Patrick Williams075c7922024-08-16 15:19:49 -0400266void DataInterface::getProperty(
267 const std::string& service, const std::string& objectPath,
268 const std::string& interface, const std::string& property,
269 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600270{
Matt Spinlera7d9d962019-11-06 15:01:25 -0600271 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
272 interface::dbusProperty, "Get");
273 method.append(interface, property);
Matt Spinler35a405b2022-03-02 11:42:42 -0600274 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlera7d9d962019-11-06 15:01:25 -0600275
276 reply.read(value);
277}
278
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600279DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
280{
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600281 auto method = _bus.new_method_call(
282 service_name::objectMapper, object_path::objectMapper,
283 interface::objectMapper, "GetSubTreePaths");
284
285 method.append(std::string{"/"}, 0, interfaces);
286
Matt Spinler35a405b2022-03-02 11:42:42 -0600287 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600288
289 DBusPathList paths;
290 reply.read(paths);
291
292 return paths;
293}
294
Matt Spinlerc8705e22019-09-11 12:36:07 -0500295DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600296 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500297{
298 auto method = _bus.new_method_call(service_name::objectMapper,
299 object_path::objectMapper,
300 interface::objectMapper, "GetObject");
301
302 method.append(objectPath, std::vector<std::string>({interface}));
303
Matt Spinler35a405b2022-03-02 11:42:42 -0600304 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlerc8705e22019-09-11 12:36:07 -0500305
306 std::map<DBusService, DBusInterfaceList> response;
307 reply.read(response);
308
309 if (!response.empty())
310 {
311 return response.begin()->first;
312 }
313
314 return std::string{};
315}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600316
Matt Spinler677381b2020-01-23 10:04:29 -0600317void DataInterface::readBMCFWVersion()
318{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500319 _bmcFWVersion =
320 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600321}
322
323void DataInterface::readServerFWVersion()
324{
Sumit Kumarcad16202021-05-13 04:06:15 -0500325 auto value =
326 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
327 if ((value != "") && (value.find_last_of(')') != std::string::npos))
328 {
329 std::size_t pos = value.find_first_of('(') + 1;
330 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
331 }
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600332}
333
Matt Spinler677381b2020-01-23 10:04:29 -0600334void DataInterface::readBMCFWVersionID()
335{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500336 _bmcFWVersionID =
337 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600338}
339
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500340std::string DataInterface::getMachineTypeModel() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600341{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500342 std::string model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600343 try
344 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500345 auto service = getService(object_path::systemInv, interface::invAsset);
346 if (!service.empty())
347 {
348 DBusValue value;
349 getProperty(service, object_path::systemInv, interface::invAsset,
350 "Model", value);
351
352 model = std::get<std::string>(value);
353 }
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600354 }
355 catch (const std::exception& e)
356 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500357 lg2::warning("Failed reading Model property from "
358 "interface: {IFACE} exception: {ERROR}",
359 "IFACE", interface::invAsset, "ERROR", e);
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600360 }
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500361
362 return model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600363}
364
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500365std::string DataInterface::getMachineSerialNumber() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600366{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500367 std::string sn;
368 try
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600369 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500370 auto service = getService(object_path::systemInv, interface::invAsset);
371 if (!service.empty())
372 {
373 DBusValue value;
374 getProperty(service, object_path::systemInv, interface::invAsset,
375 "SerialNumber", value);
376
377 sn = std::get<std::string>(value);
378 }
379 }
380 catch (const std::exception& e)
381 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500382 lg2::warning("Failed reading SerialNumber property from "
383 "interface: {IFACE} exception: {ERROR}",
384 "IFACE", interface::invAsset, "ERROR", e);
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600385 }
386
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500387 return sn;
388}
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600389
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500390std::string DataInterface::getMotherboardCCIN() const
391{
392 std::string ccin;
393
394 try
395 {
Patrick Williams075c7922024-08-16 15:19:49 -0400396 auto service =
397 getService(object_path::motherBoardInv, interface::viniRecordVPD);
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500398 if (!service.empty())
399 {
400 DBusValue value;
401 getProperty(service, object_path::motherBoardInv,
402 interface::viniRecordVPD, "CC", value);
403
404 auto cc = std::get<std::vector<uint8_t>>(value);
405 ccin = std::string{cc.begin(), cc.end()};
406 }
407 }
408 catch (const std::exception& e)
409 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500410 lg2::warning("Failed reading Motherboard CCIN property from "
411 "interface: {IFACE} exception: {ERROR}",
412 "IFACE", interface::viniRecordVPD, "ERROR", e);
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500413 }
414
415 return ccin;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600416}
417
Ben Tynere32b7e72021-05-18 12:38:40 -0500418std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
419{
420 std::vector<uint8_t> systemIM;
421
422 try
423 {
Patrick Williams075c7922024-08-16 15:19:49 -0400424 auto service =
425 getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
Ben Tynere32b7e72021-05-18 12:38:40 -0500426 if (!service.empty())
427 {
428 DBusValue value;
429 getProperty(service, object_path::motherBoardInv,
430 interface::vsbpRecordVPD, "IM", value);
431
432 systemIM = std::get<std::vector<uint8_t>>(value);
433 }
434 }
435 catch (const std::exception& e)
436 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500437 lg2::warning("Failed reading System IM property from "
438 "interface: {IFACE} exception: {ERROR}",
439 "IFACE", interface::vsbpRecordVPD, "ERROR", e);
Ben Tynere32b7e72021-05-18 12:38:40 -0500440 }
441
442 return systemIM;
443}
444
Patrick Williams075c7922024-08-16 15:19:49 -0400445void DataInterface::getHWCalloutFields(
446 const std::string& inventoryPath, std::string& fruPartNumber,
447 std::string& ccin, std::string& serialNumber) const
Matt Spinler60c4e792020-03-13 13:45:36 -0500448{
449 // For now, attempt to get all of the properties directly on the path
450 // passed in. In the future, may need to make use of an algorithm
451 // to figure out which inventory objects actually hold these
452 // interfaces in the case of non FRUs, or possibly another service
453 // will provide this info. Any missing interfaces will result
454 // in exceptions being thrown.
455
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500456 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500457
Patrick Williams075c7922024-08-16 15:19:49 -0400458 auto properties =
459 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500460
461 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
462 fruPartNumber = std::string{value.begin(), value.end()};
463
464 value = std::get<std::vector<uint8_t>>(properties["CC"]);
465 ccin = std::string{value.begin(), value.end()};
466
467 value = std::get<std::vector<uint8_t>>(properties["SN"]);
468 serialNumber = std::string{value.begin(), value.end()};
469}
470
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500471std::string
472 DataInterface::getLocationCode(const std::string& inventoryPath) const
473{
474 auto service = getService(inventoryPath, interface::locCode);
475
476 DBusValue locCode;
477 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
478 locCode);
479
480 return std::get<std::string>(locCode);
481}
482
Matt Spinler5fb24c12020-06-04 11:21:33 -0500483std::string
484 DataInterface::addLocationCodePrefix(const std::string& locationCode)
485{
486 static const std::string locationCodePrefix{"Ufcs-"};
487
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500488 // Technically there are 2 location code prefixes, Ufcs and Umts, so
489 // if it already starts with a U then don't need to do anything.
490 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500491 {
492 return locationCodePrefix + locationCode;
493 }
494
495 return locationCode;
496}
497
498std::string DataInterface::expandLocationCode(const std::string& locationCode,
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500499 uint16_t /*node*/) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500500{
Matt Spinler0d92b522021-06-16 13:28:17 -0600501 // Location codes for connectors are the location code of the FRU they are
502 // on, plus a '-Tx' segment. Remove this last segment before expanding it
503 // and then add it back in afterwards. This way, the connector doesn't have
504 // to be in the model just so that it can be expanded.
505 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
506
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500507 auto method =
508 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
509 interface::vpdManager, "GetExpandedLocationCode");
510
Matt Spinler0d92b522021-06-16 13:28:17 -0600511 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500512
Matt Spinler35a405b2022-03-02 11:42:42 -0600513 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500514
515 std::string expandedLocationCode;
516 reply.read(expandedLocationCode);
517
Matt Spinler0d92b522021-06-16 13:28:17 -0600518 if (!connectorLoc.empty())
519 {
520 expandedLocationCode += connectorLoc;
521 }
522
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500523 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500524}
525
Patrick Williams075c7922024-08-16 15:19:49 -0400526std::vector<std::string> DataInterface::getInventoryFromLocCode(
527 const std::string& locationCode, uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500528{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500529 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
530 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500531
Matt Spinler0d92b522021-06-16 13:28:17 -0600532 // Remove the connector segment, if present, so that this method call
533 // returns an inventory path that getHWCalloutFields() can be used with.
534 // (The serial number, etc, aren't stored on the connector in the
535 // inventory, and may not even be modeled.)
536 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
537
Matt Spinler2f9225a2020-08-05 12:58:49 -0500538 auto method =
539 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
540 interface::vpdManager, methodName.c_str());
541
542 if (expanded)
543 {
Matt Spinler0d92b522021-06-16 13:28:17 -0600544 method.append(baseLoc);
Matt Spinler2f9225a2020-08-05 12:58:49 -0500545 }
546 else
547 {
Matt Spinler0d92b522021-06-16 13:28:17 -0600548 method.append(addLocationCodePrefix(baseLoc), node);
Matt Spinler2f9225a2020-08-05 12:58:49 -0500549 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500550
Matt Spinler35a405b2022-03-02 11:42:42 -0600551 auto reply = _bus.call(method, dbusTimeout);
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500552
553 std::vector<sdbusplus::message::object_path> entries;
554 reply.read(entries);
555
Matt Spinlerbad056b2023-01-25 14:16:57 -0600556 std::vector<std::string> paths;
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500557
Matt Spinlerbad056b2023-01-25 14:16:57 -0600558 // Note: The D-Bus method will fail if nothing found.
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500559 std::for_each(entries.begin(), entries.end(),
Matt Spinlerbad056b2023-01-25 14:16:57 -0600560 [&paths](const auto& path) { paths.push_back(path); });
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500561
Matt Spinlerbad056b2023-01-25 14:16:57 -0600562 return paths;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500563}
564
Matt Spinler34a904c2020-08-05 14:53:28 -0500565void DataInterface::assertLEDGroup(const std::string& ledGroup,
566 bool value) const
567{
568 DBusValue variant = value;
Patrick Williams075c7922024-08-16 15:19:49 -0400569 auto method =
570 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
571 interface::dbusProperty, "Set");
Matt Spinler34a904c2020-08-05 14:53:28 -0500572 method.append(interface::ledGroup, "Asserted", variant);
Matt Spinler35a405b2022-03-02 11:42:42 -0600573 _bus.call(method, dbusTimeout);
Matt Spinler34a904c2020-08-05 14:53:28 -0500574}
575
Matt Spinler993168d2021-04-07 16:05:03 -0500576void DataInterface::setFunctional(const std::string& objectPath,
577 bool value) const
578{
Matt Spinler412f50e2023-11-14 12:49:52 -0600579 DBusPropertyMap prop{{"Functional", value}};
580 DBusInterfaceMap iface{{interface::operationalStatus, prop}};
Matt Spinler993168d2021-04-07 16:05:03 -0500581
Matt Spinler412f50e2023-11-14 12:49:52 -0600582 // PIM takes a relative path like /system/chassis so remove
583 // /xyz/openbmc_project/inventory if present.
584 std::string path{objectPath};
585 if (path.starts_with(object_path::baseInv))
586 {
587 path = objectPath.substr(strlen(object_path::baseInv));
588 }
589 DBusObjectMap object{{path, iface}};
Matt Spinler993168d2021-04-07 16:05:03 -0500590
Matt Spinler412f50e2023-11-14 12:49:52 -0600591 auto method = _bus.new_method_call(service_name::inventoryManager,
592 object_path::baseInv,
593 interface::inventoryManager, "Notify");
594 method.append(std::move(object));
Matt Spinler35a405b2022-03-02 11:42:42 -0600595 _bus.call(method, dbusTimeout);
Matt Spinler993168d2021-04-07 16:05:03 -0500596}
597
Sumit Kumar76198a22021-07-15 05:59:57 -0500598using AssociationTuple = std::tuple<std::string, std::string, std::string>;
599using AssociationsProperty = std::vector<AssociationTuple>;
600
601void DataInterface::setCriticalAssociation(const std::string& objectPath) const
602{
603 DBusValue getAssociationValue;
604
Sumit Kumar027bf282022-01-24 11:25:19 -0600605 auto service = getService(objectPath, interface::associationDef);
Sumit Kumar76198a22021-07-15 05:59:57 -0500606
Sumit Kumar027bf282022-01-24 11:25:19 -0600607 getProperty(service, objectPath, interface::associationDef, "Associations",
Sumit Kumar76198a22021-07-15 05:59:57 -0500608 getAssociationValue);
609
610 auto association = std::get<AssociationsProperty>(getAssociationValue);
611
612 AssociationTuple critAssociation{
613 "health_rollup", "critical",
614 "/xyz/openbmc_project/inventory/system/chassis"};
615
616 if (std::find(association.begin(), association.end(), critAssociation) ==
617 association.end())
618 {
619 association.push_back(critAssociation);
620 DBusValue setAssociationValue = association;
621
622 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
623 interface::dbusProperty, "Set");
624
Sumit Kumar027bf282022-01-24 11:25:19 -0600625 method.append(interface::associationDef, "Associations",
Sumit Kumar76198a22021-07-15 05:59:57 -0500626 setAssociationValue);
Matt Spinler35a405b2022-03-02 11:42:42 -0600627 _bus.call(method, dbusTimeout);
Sumit Kumar76198a22021-07-15 05:59:57 -0500628 }
629}
630
Matt Spinler1ab66962020-10-29 13:21:44 -0500631std::vector<std::string> DataInterface::getSystemNames() const
632{
633 DBusSubTree subtree;
634 DBusValue names;
635
636 auto method = _bus.new_method_call(service_name::objectMapper,
637 object_path::objectMapper,
638 interface::objectMapper, "GetSubTree");
639 method.append(std::string{"/"}, 0,
640 std::vector<std::string>{interface::compatible});
Matt Spinler35a405b2022-03-02 11:42:42 -0600641 auto reply = _bus.call(method, dbusTimeout);
Matt Spinler1ab66962020-10-29 13:21:44 -0500642
643 reply.read(subtree);
644 if (subtree.empty())
645 {
646 throw std::runtime_error("Compatible interface not on D-Bus");
647 }
648
Matt Spinlerbbc11ef2024-01-11 16:26:51 -0600649 for (const auto& [path, interfaceMap] : subtree)
650 {
651 auto iface = interfaceMap.find(service_name::entityManager);
652 if (iface == interfaceMap.end())
653 {
654 continue;
655 }
Matt Spinler1ab66962020-10-29 13:21:44 -0500656
Matt Spinlerbbc11ef2024-01-11 16:26:51 -0600657 getProperty(iface->first, path, interface::compatible, "Names", names);
Matt Spinler1ab66962020-10-29 13:21:44 -0500658
Matt Spinlerbbc11ef2024-01-11 16:26:51 -0600659 return std::get<std::vector<std::string>>(names);
660 }
661
662 throw std::runtime_error("EM Compatible interface not on D-Bus");
Matt Spinler1ab66962020-10-29 13:21:44 -0500663}
664
Sumit Kumar3b8ed7f2021-05-18 12:38:35 -0500665bool DataInterface::getQuiesceOnError() const
666{
667 bool ret = false;
668
669 try
670 {
Patrick Williams075c7922024-08-16 15:19:49 -0400671 auto service =
672 getService(object_path::logSetting, interface::logSetting);
Sumit Kumar3b8ed7f2021-05-18 12:38:35 -0500673 if (!service.empty())
674 {
675 DBusValue value;
676 getProperty(service, object_path::logSetting, interface::logSetting,
677 "QuiesceOnHwError", value);
678
679 ret = std::get<bool>(value);
680 }
681 }
682 catch (const std::exception& e)
683 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500684 lg2::warning("Failed reading QuiesceOnHwError property from "
685 "interface: {IFACE} exception: {ERROR}",
686 "IFACE", interface::logSetting, "ERROR", e);
Sumit Kumar3b8ed7f2021-05-18 12:38:35 -0500687 }
688
689 return ret;
690}
691
Sumit Kumar9d43a722021-08-24 09:46:19 -0500692std::vector<bool>
693 DataInterface::checkDumpStatus(const std::vector<std::string>& type) const
694{
695 DBusSubTree subtree;
696 std::vector<bool> result(type.size(), false);
697
698 // Query GetSubTree for the availability of dump interface
699 auto method = _bus.new_method_call(service_name::objectMapper,
700 object_path::objectMapper,
701 interface::objectMapper, "GetSubTree");
702 method.append(std::string{"/"}, 0,
703 std::vector<std::string>{interface::dumpEntry});
Matt Spinler35a405b2022-03-02 11:42:42 -0600704 auto reply = _bus.call(method, dbusTimeout);
Sumit Kumar9d43a722021-08-24 09:46:19 -0500705
706 reply.read(subtree);
707
708 if (subtree.empty())
709 {
710 return result;
711 }
712
713 std::vector<bool>::iterator itDumpStatus = result.begin();
714 uint8_t count = 0;
715 for (const auto& [path, serviceInfo] : subtree)
716 {
717 const auto& service = serviceInfo.begin()->first;
718 // Check for dump type on the object path
719 for (const auto& it : type)
720 {
721 if (path.find(it) != std::string::npos)
722 {
723 DBusValue value, progress;
724
725 // If dump type status is already available go for next path
726 if (*itDumpStatus)
727 {
728 break;
729 }
730
731 // Check for valid dump to be available if following
732 // conditions are met for the dump entry path -
733 // Offloaded == false and Status == Completed
734 getProperty(service, path, interface::dumpEntry, "Offloaded",
735 value);
736 getProperty(service, path, interface::dumpProgress, "Status",
737 progress);
738 auto offload = std::get<bool>(value);
739 auto status = std::get<std::string>(progress);
740 if (!offload && (status.find("Completed") != std::string::npos))
741 {
742 *itDumpStatus = true;
743 count++;
744 if (count >= type.size())
745 {
746 return result;
747 }
748 break;
749 }
750 }
Matt Spinler45796e82022-07-01 11:25:27 -0500751 ++itDumpStatus;
Sumit Kumar9d43a722021-08-24 09:46:19 -0500752 }
753 itDumpStatus = result.begin();
754 }
755
756 return result;
757}
758
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -0500759void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
760 const std::string& type,
761 const std::string& logPath) const
762{
763 try
764 {
765 auto method = _bus.new_method_call(
766 service_name::hwIsolation, object_path::hwIsolation,
767 interface::hwIsolationCreate, "CreateWithEntityPath");
768 method.append(binPath, type, sdbusplus::message::object_path(logPath));
769 // Note: hw isolation "CreateWithEntityPath" got dependency on logging
770 // api's. Making d-bus call no reply type to avoid cyclic dependency.
771 // Added minimal timeout to catch initial failures.
772 // Need to revisit this design later to avoid cyclic dependency.
Matt Spinler35a405b2022-03-02 11:42:42 -0600773 constexpr auto hwIsolationTimeout = 100000; // in micro seconds
774 _bus.call_noreply(method, hwIsolationTimeout);
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -0500775 }
776
Patrick Williams45e83522022-07-22 19:26:52 -0500777 catch (const sdbusplus::exception_t& e)
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -0500778 {
779 std::string errName = e.name();
780 // SD_BUS_ERROR_TIMEOUT error is expected, due to PEL api dependency
781 // mentioned above. Ignoring the error.
782 if (errName != SD_BUS_ERROR_TIMEOUT)
783 {
Matt Spinlera167a7d2023-06-30 15:14:25 -0500784 lg2::error("GUARD D-Bus call exception. Path={PATH}, "
785 "interface = {IFACE}, exception = {ERROR}",
786 "PATH", object_path::hwIsolation, "IFACE",
787 interface::hwIsolationCreate, "ERROR", e);
Jayanth Othayothecaa2fc2021-11-05 02:02:52 -0500788 }
789 }
790}
Sumit Kumar3e274432021-09-14 06:37:56 -0500791
792void DataInterface::createProgressSRC(
793 const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const
794{
795 DBusValue variant = std::make_tuple(priSRC, srcStruct);
796
797 auto method = _bus.new_method_call(service_name::bootRawProgress,
Vijay Lobo875b6c72021-10-20 17:38:56 -0500798 object_path::bootRawProgress,
Sumit Kumar3e274432021-09-14 06:37:56 -0500799 interface::dbusProperty, "Set");
800
801 method.append(interface::bootRawProgress, "Value", variant);
802
Matt Spinler35a405b2022-03-02 11:42:42 -0600803 _bus.call(method, dbusTimeout);
Sumit Kumar3e274432021-09-14 06:37:56 -0500804}
Sumit Kumar027bf282022-01-24 11:25:19 -0600805
806std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
807{
808 std::vector<std::string> association = {"xyz.openbmc_project.Association"};
809 std::string hwErrorLog = "/isolated_hw_errorlog";
810 std::string errorLog = "/error_log";
811 DBusPathList paths;
812 std::vector<uint32_t> ids;
813
814 // Get all latest mapper associations
815 paths = getPaths(association);
816 for (auto& path : paths)
817 {
818 // Look for object path with hardware isolation entry if any
819 size_t pos = path.find(hwErrorLog);
820 if (pos != std::string::npos)
821 {
822 // Get the object path
823 std::string ph = path;
824 ph.erase(pos, hwErrorLog.length());
825 auto service = getService(ph, interface::hwIsolationEntry);
826 if (!service.empty())
827 {
828 bool status;
829 DBusValue value;
830
831 // Read the Resolved property from object path
832 getProperty(service, ph, interface::hwIsolationEntry,
833 "Resolved", value);
834
835 status = std::get<bool>(value);
836
837 // If the entry isn't resolved
838 if (!status)
839 {
Patrick Williams075c7922024-08-16 15:19:49 -0400840 auto assocService =
841 getService(path, interface::association);
Matt Spinler45796e82022-07-01 11:25:27 -0500842 if (!assocService.empty())
Sumit Kumar027bf282022-01-24 11:25:19 -0600843 {
Matt Spinler45796e82022-07-01 11:25:27 -0500844 DBusValue endpoints;
Sumit Kumar027bf282022-01-24 11:25:19 -0600845
846 // Read Endpoints property
Matt Spinler45796e82022-07-01 11:25:27 -0500847 getProperty(assocService, path, interface::association,
848 "endpoints", endpoints);
Sumit Kumar027bf282022-01-24 11:25:19 -0600849
850 auto logPath =
Matt Spinler45796e82022-07-01 11:25:27 -0500851 std::get<std::vector<std::string>>(endpoints);
Sumit Kumar027bf282022-01-24 11:25:19 -0600852 if (!logPath.empty())
853 {
854 // Get OpenBMC event log Id
855 uint32_t id = stoi(logPath[0].substr(
856 logPath[0].find_last_of('/') + 1));
857 ids.push_back(id);
858 }
859 }
860 }
861 }
862 }
863
864 // Look for object path with error_log entry if any
865 pos = path.find(errorLog);
866 if (pos != std::string::npos)
867 {
868 auto service = getService(path, interface::association);
869 if (!service.empty())
870 {
871 DBusValue value;
872
873 // Read Endpoints property
874 getProperty(service, path, interface::association, "endpoints",
875 value);
876
877 auto logPath = std::get<std::vector<std::string>>(value);
878 if (!logPath.empty())
879 {
880 // Get OpenBMC event log Id
881 uint32_t id = stoi(
882 logPath[0].substr(logPath[0].find_last_of('/') + 1));
883 ids.push_back(id);
884 }
885 }
886 }
887 }
888
889 if (ids.size() > 1)
890 {
891 // remove duplicates to have only unique ids
892 std::sort(ids.begin(), ids.end());
893 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
894 }
895 return ids;
896}
Vijay Lobo875b6c72021-10-20 17:38:56 -0500897
898std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
899{
900 using RawProgressProperty = std::tuple<uint64_t, std::vector<uint8_t>>;
901
902 DBusValue value;
903 getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
904 interface::bootRawProgress, "Value", value);
905
906 const auto& rawProgress = std::get<RawProgressProperty>(value);
907 return std::get<1>(rawProgress);
908}
909
Arya K Padmand8ae6182024-07-19 06:25:10 -0500910std::optional<std::vector<uint8_t>>
911 DataInterface::getDIProperty(const std::string& locationCode) const
912{
913 std::vector<uint8_t> viniDI;
914
915 try
916 {
917 // Note : The hardcoded value 0 should be changed when comes to
918 // multinode system.
919 auto objectPath = getInventoryFromLocCode(locationCode, 0, true);
920
921 DBusValue value;
922 getProperty(service_name::inventoryManager, objectPath[0],
923 interface::viniRecordVPD, "DI", value);
924
925 viniDI = std::get<std::vector<uint8_t>>(value);
926 }
927 catch (const std::exception& e)
928 {
929 lg2::warning(
930 "Failed reading DI property for the location code : {LOC_CODE} from "
931 "interface: {IFACE} exception: {ERROR}",
932 "LOC_CODE", locationCode, "IFACE", interface::viniRecordVPD,
933 "ERROR", e);
934 return std::nullopt;
935 }
936
937 return viniDI;
938}
939
940std::optional<bool>
941 DataInterfaceBase::isDIMMLocCode(const std::string& locCode) const
942{
943 if (_locationCache.contains(locCode))
944 {
945 return _locationCache.at(locCode);
946 }
947 else
948 {
949 return std::nullopt;
950 }
951}
952
953void DataInterfaceBase::addDIMMLocCode(const std::string& locCode,
954 bool isFRUDIMM)
955{
956 _locationCache.insert({locCode, isFRUDIMM});
957}
958
959std::expected<bool, std::string>
960 DataInterfaceBase::isDIMM(const std::string& locCode)
961{
962 auto isDIMMType = isDIMMLocCode(locCode);
963 if (isDIMMType.has_value())
964 {
965 return isDIMMType.value();
966 }
967#ifndef PEL_ENABLE_PHAL
968 return std::unexpected<std::string>(
969 std::format("PHAL feature is not enabled, so the LocationCode:[{}] "
970 "cannot be determined as DIMM",
971 locCode));
972#else
973 else
974 {
975 // Invoke pHAL API inorder to fetch the FRU Type
976 auto fruType = openpower::phal::pdbg::getFRUType(locCode);
977 if (fruType.has_value())
978 {
979 bool isDIMMFRU{false};
980 if (fruType.value() == ENUM_ATTR_TYPE_DIMM)
981 {
982 isDIMMFRU = true;
983 }
984 addDIMMLocCode(locCode, isDIMMFRU);
985 return isDIMMFRU;
986 }
987 else
988 {
989 std::string msg{std::format("Failed to determine the HW Type, "
990 "LocationCode:[{}]",
991 locCode)};
992 if (openpower::phal::exception::errMsgMap.contains(fruType.error()))
993 {
994 msg = std::format(
995 "{} PHALErrorMsg:[{}]", msg,
996 openpower::phal::exception::errMsgMap.at(fruType.error()));
997 }
998 else
999 {
1000 msg = std::format(
1001 "{} PHALErrorMsg:[Unknown PHALErrorCode:{}]", msg,
1002 std::to_underlying<openpower::phal::exception::ERR_TYPE>(
1003 fruType.error()));
1004 }
1005 return std::unexpected<std::string>(msg);
1006 }
1007 }
1008#endif
1009}
1010
Matt Spinler5b423652023-05-04 13:08:44 -05001011void DataInterface::startFruPlugWatch()
1012{
1013 // Add a watch on inventory InterfacesAdded and then find all
1014 // existing hotpluggable interfaces and add propertiesChanged
1015 // watches on them.
1016
1017 _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
1018 _bus, match_rules::interfacesAdded(object_path::baseInv),
1019 std::bind(&DataInterface::inventoryIfaceAdded, this,
1020 std::placeholders::_1));
1021 try
1022 {
1023 auto paths = getPaths(hotplugInterfaces);
1024
1025 _invPresentMatches.clear();
1026
1027 std::for_each(paths.begin(), paths.end(),
1028 [this](const auto& path) { addHotplugWatch(path); });
1029 }
1030 catch (const sdbusplus::exception_t& e)
1031 {
Matt Spinlera167a7d2023-06-30 15:14:25 -05001032 lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
Matt Spinler5b423652023-05-04 13:08:44 -05001033 }
1034}
1035
1036void DataInterface::addHotplugWatch(const std::string& path)
1037{
1038 if (!_invPresentMatches.contains(path))
1039 {
1040 _invPresentMatches.emplace(
1041 path,
1042 std::make_unique<sdbusplus::bus::match_t>(
1043 _bus, match_rules::propertiesChanged(path, interface::invItem),
1044 std::bind(&DataInterface::presenceChanged, this,
1045 std::placeholders::_1)));
1046 }
1047}
1048
1049void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
1050{
1051 sdbusplus::message::object_path path;
1052 DBusInterfaceMap interfaces;
1053
1054 msg.read(path, interfaces);
1055
1056 // Check if any of the new interfaces are for hot pluggable FRUs.
1057 if (std::find_if(interfaces.begin(), interfaces.end(),
1058 [](const auto& interfacePair) {
Patrick Williams075c7922024-08-16 15:19:49 -04001059 return std::find(hotplugInterfaces.begin(),
1060 hotplugInterfaces.end(),
1061 interfacePair.first) !=
1062 hotplugInterfaces.end();
1063 }) == interfaces.end())
Matt Spinler5b423652023-05-04 13:08:44 -05001064 {
1065 return;
1066 }
1067
1068 addHotplugWatch(path.str);
1069
1070 // If an Inventory.Item interface was also added, check presence now.
1071
1072 // Notes:
1073 // * This assumes the Inv.Item and Inv.Fan/PS are added together which
1074 // is currently the case.
1075 // * If the code ever switches to something without a Present
1076 // property, then the IA signal itself would probably indicate presence.
1077
1078 auto itemIt = interfaces.find(interface::invItem);
1079 if (itemIt != interfaces.end())
1080 {
1081 notifyPresenceSubsribers(path.str, itemIt->second);
1082 }
1083}
1084
1085void DataInterface::presenceChanged(sdbusplus::message_t& msg)
1086{
1087 DBusInterface interface;
1088 DBusPropertyMap properties;
1089
1090 msg.read(interface, properties);
1091 if (interface != interface::invItem)
1092 {
1093 return;
1094 }
1095
1096 std::string path = msg.get_path();
1097 notifyPresenceSubsribers(path, properties);
1098}
1099
1100void DataInterface::notifyPresenceSubsribers(const std::string& path,
1101 const DBusPropertyMap& properties)
1102{
Matt Spinler5ee36052023-05-30 14:20:56 -05001103 auto prop = properties.find("Present");
1104 if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
1105 {
1106 return;
1107 }
1108
1109 std::string locCode;
1110
Matt Spinler5b423652023-05-04 13:08:44 -05001111 try
1112 {
Matt Spinler52ee3a42023-07-27 14:54:48 -05001113 auto service = getService(path, interface::locCode);
1114
1115 // If the hotplugged FRU is hosted by PLDM, then it is
1116 // in an IO expansion drawer and we don't care about it.
1117 if (service == service_name::pldm)
1118 {
1119 return;
1120 }
1121
Matt Spinler5ee36052023-05-30 14:20:56 -05001122 locCode = getLocationCode(path);
Matt Spinler5b423652023-05-04 13:08:44 -05001123 }
Matt Spinler5ee36052023-05-30 14:20:56 -05001124 catch (const sdbusplus::exception_t& e)
Matt Spinler5b423652023-05-04 13:08:44 -05001125 {
Matt Spinlera167a7d2023-06-30 15:14:25 -05001126 lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1127 path, "ERROR", e);
Matt Spinler5ee36052023-05-30 14:20:56 -05001128 return;
Matt Spinler5b423652023-05-04 13:08:44 -05001129 }
Matt Spinler5ee36052023-05-30 14:20:56 -05001130
Matt Spinlera167a7d2023-06-30 15:14:25 -05001131 lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1132 locCode);
Matt Spinler5ee36052023-05-30 14:20:56 -05001133
1134 // Tell the subscribers.
1135 setFruPresent(locCode);
Matt Spinler5b423652023-05-04 13:08:44 -05001136}
Arya K Padmanafba3162024-08-30 07:41:32 -05001137
1138#ifdef PEL_ENABLE_PHAL
1139bool DataInterface::isPHALDevTreeExist() const
1140{
1141 try
1142 {
1143 if (std::filesystem::exists(PDBG_DTB_PATH))
1144 {
1145 return true;
1146 }
1147 }
1148 catch (const std::exception& e)
1149 {
1150 lg2::error("Failed to check device tree {PHAL_DEVTREE_PATH} existence, "
1151 "{ERROR}",
1152 "PHAL_DEVTREE_PATH", PDBG_DTB_PATH, "ERROR", e);
1153 }
1154 return false;
1155}
1156
1157void DataInterface::initPHAL()
1158{
1159 if (setenv("PDBG_DTB", PDBG_DTB_PATH, 1))
1160 {
1161 // Log message and continue,
1162 // This is to help continue creating PEL in raw format.
1163 lg2::error("Failed to set PDBG_DTB: ({ERRNO})", "ERRNO",
1164 strerror(errno));
1165 }
1166
1167 if (!pdbg_targets_init(NULL))
1168 {
1169 lg2::error("pdbg_targets_init failed");
1170 return;
1171 }
1172
1173 if (libekb_init())
1174 {
1175 lg2::error("libekb_init failed, skipping ffdc processing");
1176 return;
1177 }
1178}
1179
1180void DataInterface::subscribeToSystemdSignals()
1181{
1182 try
1183 {
1184 auto method =
1185 _bus.new_method_call(service_name::systemd, object_path::systemd,
1186 interface::systemdMgr, "Subscribe");
1187 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1188 // Initializing with nullptr to indicate that it is not subscribed
1189 // to any signal.
1190 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1191 if (msg.is_method_error())
1192 {
1193 auto* error = msg.get_error();
1194 lg2::error("Failed to subscribe JobRemoved systemd signal, "
1195 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG}, "
1196 "ERR_NAME",
1197 error->name, "ERR_MSG", error->message);
1198 return;
1199 }
1200
1201 namespace sdbusRule = sdbusplus::bus::match::rules;
1202 this->_systemdMatch =
1203 std::make_unique<decltype(this->_systemdMatch)::element_type>(
1204 this->_bus,
1205 sdbusRule::type::signal() +
1206 sdbusRule::member("JobRemoved") +
1207 sdbusRule::path(object_path::systemd) +
1208 sdbusRule::interface(interface::systemdMgr),
1209 [this](sdbusplus::message_t& msg) {
1210 uint32_t jobID;
1211 sdbusplus::message::object_path jobObjPath;
1212 std::string jobUnitName, jobUnitResult;
1213
1214 msg.read(jobID, jobObjPath, jobUnitName, jobUnitResult);
1215 if ((jobUnitName ==
1216 "openpower-update-bios-attr-table.service") &&
1217 (jobUnitResult == "done"))
1218 {
1219 // Unsubscribe immediately after the signal is
1220 // received to avoid unwanted signal
1221 // reception. And initialize PHAL once the
1222 // unsubscription is success.
1223 this->unsubscribeFromSystemdSignals();
1224 }
1225 });
1226 });
1227 }
1228 catch (const sdbusplus::exception_t& e)
1229 {
1230 lg2::error(
1231 "Exception occured while handling JobRemoved systemd signal, "
1232 "exception: {ERROR}",
1233 "ERROR", e);
1234 }
1235}
1236
1237void DataInterface::unsubscribeFromSystemdSignals()
1238{
1239 try
1240 {
1241 auto method =
1242 _bus.new_method_call(service_name::systemd, object_path::systemd,
1243 interface::systemdMgr, "Unsubscribe");
1244 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1245 // Unsubscribing the _systemdSlot from the subscribed signal
1246 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1247 if (msg.is_method_error())
1248 {
1249 auto* error = msg.get_error();
1250 lg2::error(
1251 "Failed to unsubscribe from JobRemoved systemd signal, "
1252 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG}, "
1253 "ERR_NAME",
1254 error->name, "ERR_MSG", error->message);
1255 return;
1256 }
1257 this->initPHAL();
1258 });
1259 }
1260 catch (const sdbusplus::exception_t& e)
1261 {
1262 lg2::error(
1263 "Exception occured while unsubscribing from JobRemoved systemd signal, "
1264 "exception: {ERROR}",
1265 "ERROR", e);
1266 }
1267}
1268#endif // PEL_ENABLE_PHAL
1269
Matt Spinlerc8705e22019-09-11 12:36:07 -05001270} // namespace pels
1271} // namespace openpower