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