1 #include <map>
2 #include <phosphor-logging/elog-errors.hpp>
3 #include "xyz/openbmc_project/Common/error.hpp"
4 #include "read_fru_data.hpp"
5 #include "fruread.hpp"
6 #include "host-ipmid/ipmid-api.h"
7 #include "utils.hpp"
8 #include "types.hpp"
9 
10 extern const FruMap frus;
11 namespace ipmi
12 {
13 namespace fru
14 {
15 using namespace phosphor::logging;
16 using InternalFailure =
17         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
18 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr);
19 
20 static constexpr auto INV_INTF  = "xyz.openbmc_project.Inventory.Manager";
21 static constexpr auto OBJ_PATH  = "/xyz/openbmc_project/inventory";
22 static constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties";
23 
24 namespace cache
25 {
26     //User initiate read FRU info area command followed by
27     //FRU read command. Also data is read in small chunks of
28     //the specified offset and count.
29     //Caching the data which will be invalidated when ever there
30     //is a change in FRU properties.
31     FRUAreaMap fruMap;
32 }
33 /**
34  * @brief Read all the property value's for the specified interface
35  *  from Inventory.
36  *
37  * @param[in] intf Interface
38  * @param[in] path Object path
39  * @return map of properties
40  */
41 ipmi::PropertyMap readAllProperties(const std::string& intf,
42                                   const std::string& path)
43 {
44     ipmi::PropertyMap properties;
45     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
46     auto service = ipmi::getService(bus, INV_INTF, OBJ_PATH);
47     std::string objPath = OBJ_PATH + path;
48     auto method = bus.new_method_call(service.c_str(),
49                                       objPath.c_str(),
50                                       PROP_INTF,
51                                       "GetAll");
52     method.append(intf);
53     auto reply = bus.call(method);
54     if (reply.is_method_error())
55     {
56         //If property is not found simply return empty value
57         log<level::ERR>("Error in reading property values from inventory",
58                         entry("INTERFACE=%s", intf),
59                         entry("PATH=%s", objPath));
60         return properties;
61     }
62     reply.read(properties);
63     return properties;
64 }
65 
66 void processFruPropChange(sdbusplus::message::message& msg)
67 {
68     if(cache::fruMap.empty())
69     {
70         return;
71     }
72     std::string path = msg.get_path();
73     //trim the object base path, if found at the beginning
74     if (path.compare(0, strlen(OBJ_PATH), OBJ_PATH) == 0)
75     {
76         path.erase(0, strlen(OBJ_PATH));
77     }
78     for (auto& fru : frus)
79     {
80         bool found = false;
81         auto& fruId = fru.first;
82         auto& instanceList = fru.second;
83         for (auto& instance : instanceList)
84         {
85             if(instance.path == path)
86             {
87                 found = true;
88                 break;
89             }
90         }
91         if (found)
92         {
93             cache::fruMap.erase(fruId);
94             break;
95         }
96     }
97 }
98 
99 //register for fru property change
100 int registerCallbackHandler()
101 {
102     if(matchPtr == nullptr)
103     {
104         using namespace sdbusplus::bus::match::rules;
105         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
106         matchPtr = std::make_unique<sdbusplus::bus::match_t>(
107             bus,
108             path_namespace(OBJ_PATH) +
109             type::signal() +
110             member("PropertiesChanged") +
111             interface(PROP_INTF),
112             std::bind(processFruPropChange, std::placeholders::_1));
113     }
114     return 0;
115 }
116 
117 /**
118  * @brief Read FRU property values from Inventory
119  *
120  * @param[in] fruNum  FRU id
121  * @return populate FRU Inventory data
122  */
123 FruInventoryData readDataFromInventory(const FRUId& fruNum)
124 {
125     auto iter = frus.find(fruNum);
126     if (iter == frus.end())
127     {
128         log<level::ERR>("Unsupported FRU ID ",entry("FRUID=%d", fruNum));
129         elog<InternalFailure>();
130     }
131 
132     FruInventoryData data;
133     auto& instanceList = iter->second;
134     for (auto& instance : instanceList)
135     {
136         for (auto& intf : instance.interfaces)
137         {
138             ipmi::PropertyMap allProp = readAllProperties(
139                     intf.first, instance.path);
140             for (auto& properties : intf.second)
141             {
142                 auto iter = allProp.find(properties.first);
143                 if (iter != allProp.end())
144                 {
145                     data[properties.second.section].emplace(properties.first,
146                         std::move(allProp[properties.first].get<std::string>()));
147                 }
148             }
149         }
150     }
151     return data;
152 }
153 
154 const FruAreaData& getFruAreaData(const FRUId& fruNum)
155 {
156     auto iter = cache::fruMap.find(fruNum);
157     if (iter != cache::fruMap.end())
158     {
159         return iter->second;
160     }
161     auto invData = readDataFromInventory(fruNum);
162 
163     //Build area info based on inventory data
164     FruAreaData newdata = buildFruAreaData(std::move(invData));
165     cache::fruMap.emplace(fruNum, std::move(newdata));
166     return cache::fruMap.at(fruNum);
167 }
168 } //fru
169 } //ipmi
170