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 
9 extern const FruMap frus;
10 namespace ipmi
11 {
12 namespace fru
13 {
14 using namespace phosphor::logging;
15 using InternalFailure =
16         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
17 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr);
18 
19 static constexpr auto INV_INTF  = "xyz.openbmc_project.Inventory.Manager";
20 static constexpr auto OBJ_PATH  = "/xyz/openbmc_project/inventory";
21 static constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties";
22 
23 namespace cache
24 {
25     //User initiate read FRU info area command followed by
26     //FRU read command. Also data is read in small chunks of
27     //the specified offset and count.
28     //Caching the data which will be invalidated when ever there
29     //is a change in FRU properties.
30     FRUAreaMap fruMap;
31 }
32 /**
33  * @brief Read the property value from Inventory
34  *
35  * @param[in] bus dbus
36  * @param[in] intf Interface
37  * @param[in] propertyName Name of the property
38  * @param[in] path Object path
39  * @return property value
40  */
41 std::string readProperty(const std::string& intf,
42                          const std::string& propertyName,
43                          const std::string& path)
44 {
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                                       "Get");
52     method.append(intf, propertyName);
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::INFO>("Property value not set",
58             entry("Property=%s", propertyName),
59             entry("Path=%s", objPath));
60         return {};
61     }
62     sdbusplus::message::variant<std::string> property;
63     reply.read(property);
64     std::string value =
65         sdbusplus::message::variant_ns::get<std::string>(property);
66     return value;
67 }
68 
69 void processFruPropChange(sdbusplus::message::message& msg)
70 {
71     if(cache::fruMap.empty())
72     {
73         return;
74     }
75     std::string path = msg.get_path();
76     //trim the object base path, if found at the beginning
77     if (path.compare(0, strlen(OBJ_PATH), OBJ_PATH) == 0)
78     {
79         path.erase(0, strlen(OBJ_PATH));
80     }
81     for (auto& fru : frus)
82     {
83         bool found = false;
84         auto& fruId = fru.first;
85         auto& instanceList = fru.second;
86         for (auto& instance : instanceList)
87         {
88             if(instance.first == path)
89             {
90                 found = true;
91                 break;
92             }
93         }
94         if (found)
95         {
96             cache::fruMap.erase(fruId);
97             break;
98         }
99     }
100 }
101 
102 //register for fru property change
103 int registerCallbackHandler()
104 {
105     if(matchPtr == nullptr)
106     {
107         using namespace sdbusplus::bus::match::rules;
108         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
109         matchPtr = std::make_unique<sdbusplus::bus::match_t>(
110             bus,
111             path_namespace(OBJ_PATH) +
112             type::signal() +
113             member("PropertiesChanged") +
114             interface(PROP_INTF),
115             std::bind(processFruPropChange, std::placeholders::_1));
116     }
117     return 0;
118 }
119 
120 /**
121  * @brief Read FRU property values from Inventory
122  *
123  * @param[in] fruNum  FRU id
124  * @return populate FRU Inventory data
125  */
126 FruInventoryData readDataFromInventory(const FRUId& fruNum)
127 {
128     auto iter = frus.find(fruNum);
129     if (iter == frus.end())
130     {
131         log<level::ERR>("Unsupported FRU ID ",entry("FRUID=%d", fruNum));
132         elog<InternalFailure>();
133     }
134 
135     FruInventoryData data;
136     auto& instanceList = iter->second;
137     for (auto& instance : instanceList)
138     {
139         for (auto& interfaceList : instance.second)
140         {
141             for (auto& properties : interfaceList.second)
142             {
143                 decltype(auto) pdata = properties.second;
144                 auto value = readProperty(
145                         interfaceList.first, properties.first,
146                         instance.first);
147                 data[pdata.section].emplace(properties.first, value);
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