xref: /openbmc/openpower-vpd-parser/vpd-manager/manager.cpp (revision a4a2adfa96d2c491b1baf776013a6442bc88c78b)
1 #include "config.h"
2 
3 #include "manager.hpp"
4 
5 #include "common_utility.hpp"
6 #include "editor_impl.hpp"
7 #include "gpioMonitor.hpp"
8 #include "ibm_vpd_utils.hpp"
9 #include "ipz_parser.hpp"
10 #include "reader_impl.hpp"
11 #include "vpd_exceptions.hpp"
12 
13 #include <filesystem>
14 #include <phosphor-logging/elog-errors.hpp>
15 
16 using namespace openpower::vpd::manager;
17 using namespace openpower::vpd::constants;
18 using namespace openpower::vpd::inventory;
19 using namespace openpower::vpd::manager::editor;
20 using namespace openpower::vpd::manager::reader;
21 using namespace std;
22 using namespace openpower::vpd::parser;
23 using namespace openpower::vpd::exceptions;
24 using namespace phosphor::logging;
25 
26 namespace openpower
27 {
28 namespace vpd
29 {
30 namespace manager
31 {
32 Manager::Manager(sdbusplus::bus::bus&& bus, const char* busName,
33                  const char* objPath, const char* /*iFace*/) :
34     ServerObject<ManagerIface>(bus, objPath),
35     _bus(std::move(bus)), _manager(_bus, objPath)
36 {
37     _bus.request_name(busName);
38 }
39 
40 void Manager::run()
41 {
42     try
43     {
44         processJSON();
45         listenHostState();
46         listenAssetTag();
47 
48         auto event = sdeventplus::Event::get_default();
49         GpioMonitor gpioMon1(jsonFile, event);
50 
51         _bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
52         cout << "VPD manager event loop started\n";
53         event.loop();
54     }
55     catch (const std::exception& e)
56     {
57         std::cerr << e.what() << "\n";
58     }
59 }
60 
61 void Manager::listenHostState()
62 {
63     static std::shared_ptr<sdbusplus::bus::match::match> hostState =
64         std::make_shared<sdbusplus::bus::match::match>(
65             _bus,
66             sdbusplus::bus::match::rules::propertiesChanged(
67                 "/xyz/openbmc_project/state/host0",
68                 "xyz.openbmc_project.State.Host"),
69             [this](sdbusplus::message::message& msg) {
70                 hostStateCallBack(msg);
71             });
72 }
73 
74 void Manager::hostStateCallBack(sdbusplus::message::message& msg)
75 {
76     if (msg.is_method_error())
77     {
78         std::cerr << "Error in reading signal " << std::endl;
79     }
80 
81     Path object;
82     PropertyMap propMap;
83     msg.read(object, propMap);
84     const auto itr = propMap.find("CurrentHostState");
85     if (itr != propMap.end())
86     {
87         if (auto hostState = std::get_if<std::string>(&(itr->second)))
88         {
89             // implies system is moving from standby to power on state
90             if (*hostState == "xyz.openbmc_project.State.Host.HostState."
91                               "TransitioningToRunning")
92             {
93                 // check and perfrom recollection for FRUs replaceable at
94                 // standby.
95                 performVPDRecollection();
96                 return;
97             }
98         }
99         std::cerr << "Failed to read Host state" << std::endl;
100     }
101 }
102 
103 void Manager::listenAssetTag()
104 {
105     static std::shared_ptr<sdbusplus::bus::match::match> assetMatcher =
106         std::make_shared<sdbusplus::bus::match::match>(
107             _bus,
108             sdbusplus::bus::match::rules::propertiesChanged(
109                 "/xyz/openbmc_project/inventory/system",
110                 "xyz.openbmc_project.Inventory.Decorator.AssetTag"),
111             [this](sdbusplus::message::message& msg) {
112                 assetTagCallback(msg);
113             });
114 }
115 
116 void Manager::assetTagCallback(sdbusplus::message::message& msg)
117 {
118     if (msg.is_method_error())
119     {
120         std::cerr << "Error in reading signal " << std::endl;
121     }
122 
123     Path object;
124     PropertyMap propMap;
125     msg.read(object, propMap);
126     const auto itr = propMap.find("AssetTag");
127     if (itr != propMap.end())
128     {
129         if (auto assetTag = std::get_if<std::string>(&(itr->second)))
130         {
131             // Call Notify to persist the AssetTag
132             inventory::ObjectMap objectMap = {
133                 {std::string{"/system"},
134                  {{"xyz.openbmc_project.Inventory.Decorator.AssetTag",
135                    {{"AssetTag", *assetTag}}}}}};
136 
137             common::utility::callPIM(std::move(objectMap));
138         }
139         else
140         {
141             std::cerr << "Failed to read asset tag" << std::endl;
142         }
143     }
144 }
145 
146 void Manager::processJSON()
147 {
148     std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary);
149 
150     if (!json)
151     {
152         throw std::runtime_error("json file not found");
153     }
154 
155     jsonFile = nlohmann::json::parse(json);
156     if (jsonFile.find("frus") == jsonFile.end())
157     {
158         throw std::runtime_error("frus group not found in json");
159     }
160 
161     const nlohmann::json& groupFRUS =
162         jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
163     for (const auto& itemFRUS : groupFRUS.items())
164     {
165         const std::vector<nlohmann::json>& groupEEPROM =
166             itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
167         for (const auto& itemEEPROM : groupEEPROM)
168         {
169             bool isMotherboard = false;
170             std::string redundantPath;
171 
172             if (itemEEPROM["extraInterfaces"].find(
173                     "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
174                 itemEEPROM["extraInterfaces"].end())
175             {
176                 isMotherboard = true;
177             }
178             if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
179             {
180                 redundantPath = itemEEPROM["redundantEeprom"]
181                                     .get_ref<const nlohmann::json::string_t&>();
182             }
183             frus.emplace(
184                 itemEEPROM["inventoryPath"]
185                     .get_ref<const nlohmann::json::string_t&>(),
186                 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
187 
188             if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
189                 itemEEPROM["extraInterfaces"].end())
190             {
191                 fruLocationCode.emplace(
192                     itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
193                               ["LocationCode"]
194                                   .get_ref<const nlohmann::json::string_t&>(),
195                     itemEEPROM["inventoryPath"]
196                         .get_ref<const nlohmann::json::string_t&>());
197             }
198 
199             if (itemEEPROM.value("replaceableAtStandby", false))
200             {
201                 replaceableFrus.emplace_back(itemFRUS.key());
202             }
203         }
204     }
205 }
206 
207 void Manager::writeKeyword(const sdbusplus::message::object_path path,
208                            const std::string recordName,
209                            const std::string keyword, const Binary value)
210 {
211     try
212     {
213         std::string objPath{path};
214         // Strip any inventory prefix in path
215         if (objPath.find(INVENTORY_PATH) == 0)
216         {
217             objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
218         }
219 
220         if (frus.find(objPath) == frus.end())
221         {
222             throw std::runtime_error("Inventory path not found");
223         }
224 
225         inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
226 
227         // instantiate editor class to update the data
228         EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
229 
230         uint32_t offset = 0;
231         // Setup offset, if any
232         for (const auto& item : jsonFile["frus"][vpdFilePath])
233         {
234             if (item.find("offset") != item.end())
235             {
236                 offset = item["offset"];
237                 break;
238             }
239         }
240 
241         edit.updateKeyword(value, offset, true);
242 
243         // If we have a redundant EEPROM to update, then update just the EEPROM,
244         // not the cache since that is already done when we updated the primary
245         if (!std::get<1>(frus.find(objPath)->second).empty())
246         {
247             EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
248                             recordName, keyword);
249             edit.updateKeyword(value, offset, false);
250         }
251 
252         // if it is a motehrboard FRU need to check for location expansion
253         if (std::get<2>(frus.find(objPath)->second))
254         {
255             if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
256             {
257                 edit.expandLocationCode("fcs");
258             }
259             else if (recordName == "VSYS" &&
260                      (keyword == "TM" || keyword == "SE"))
261             {
262                 edit.expandLocationCode("mts");
263             }
264         }
265 
266         return;
267     }
268     catch (const std::exception& e)
269     {
270         std::cerr << e.what() << std::endl;
271     }
272 }
273 
274 ListOfPaths
275     Manager::getFRUsByUnexpandedLocationCode(const LocationCode locationCode,
276                                              const NodeNumber nodeNumber)
277 {
278     ReaderImpl read;
279     return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
280 }
281 
282 ListOfPaths
283     Manager::getFRUsByExpandedLocationCode(const LocationCode locationCode)
284 {
285     ReaderImpl read;
286     return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode);
287 }
288 
289 LocationCode Manager::getExpandedLocationCode(const LocationCode locationCode,
290                                               const NodeNumber nodeNumber)
291 {
292     ReaderImpl read;
293     return read.getExpandedLocationCode(locationCode, nodeNumber,
294                                         fruLocationCode);
295 }
296 
297 void Manager::performVPDRecollection()
298 {
299     // get list of FRUs replaceable at standby
300     for (const auto& item : replaceableFrus)
301     {
302         const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
303         const nlohmann::json& singleFru = groupEEPROM[0];
304 
305         const string& inventoryPath =
306             singleFru["inventoryPath"]
307                 .get_ref<const nlohmann::json::string_t&>();
308 
309         bool prePostActionRequired = false;
310 
311         if ((jsonFile["frus"][item].at(0)).find("preAction") !=
312             jsonFile["frus"][item].at(0).end())
313         {
314             if (!executePreAction(jsonFile, item))
315             {
316                 // if the FRU has preAction defined then its execution should
317                 // pass to ensure bind/unbind of data.
318                 // preAction execution failed. should not call bind/unbind.
319                 log<level::ERR>(
320                     "Pre-Action execution failed for the FRU",
321                     entry("ERROR=%s",
322                           ("Inventory path: " + inventoryPath).c_str()));
323                 continue;
324             }
325             prePostActionRequired = true;
326         }
327 
328         if ((singleFru.find("devAddress") == singleFru.end()) ||
329             (singleFru.find("driverType") == singleFru.end()) ||
330             (singleFru.find("busType") == singleFru.end()))
331         {
332             // The FRUs is marked for replacement but missing mandatory
333             // fields for recollection. Skip to another replaceable fru.
334             log<level::ERR>(
335                 "Recollection Failed as mandatory field missing in Json",
336                 entry("ERROR=%s",
337                       ("Recollection failed for " + inventoryPath).c_str()));
338             continue;
339         }
340 
341         string str = "echo ";
342         string deviceAddress = singleFru["devAddress"];
343         const string& driverType = singleFru["driverType"];
344         const string& busType = singleFru["busType"];
345 
346         // devTreeStatus flag is present in json as false to mention
347         // that the EEPROM is not mentioned in device tree. If this flag
348         // is absent consider the value to be true, i.e EEPROM is
349         // mentioned in device tree
350         if (!singleFru.value("devTreeStatus", true))
351         {
352             auto pos = deviceAddress.find('-');
353             if (pos != string::npos)
354             {
355                 string busNum = deviceAddress.substr(0, pos);
356                 deviceAddress =
357                     "0x" + deviceAddress.substr(pos + 1, string::npos);
358 
359                 string deleteDevice = str + deviceAddress + " > /sys/bus/" +
360                                       busType + "/devices/" + busType + "-" +
361                                       busNum + "/delete_device";
362                 executeCmd(deleteDevice);
363 
364                 string addDevice = str + driverType + " " + deviceAddress +
365                                    " > /sys/bus/" + busType + "/devices/" +
366                                    busType + "-" + busNum + "/new_device";
367                 executeCmd(addDevice);
368             }
369             else
370             {
371                 log<level::ERR>(
372                     "Wrong format of device address in Json",
373                     entry(
374                         "ERROR=%s",
375                         ("Recollection failed for " + inventoryPath).c_str()));
376                 continue;
377             }
378         }
379         else
380         {
381             executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
382                                                   driverType, "/unbind"));
383             executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
384                                                   driverType, "/bind"));
385         }
386 
387         // this check is added to avoid file system expensive call in case not
388         // required.
389         if (prePostActionRequired)
390         {
391             // Check if device showed up (test for file)
392             if (!filesystem::exists(item))
393             {
394                 // If not, then take failure postAction
395                 executePostFailAction(jsonFile, item);
396             }
397         }
398     }
399 }
400 
401 } // namespace manager
402 } // namespace vpd
403 } // namespace openpower