1*3f7c5e40SJason M. Bills /*
2*3f7c5e40SJason M. Bills // Copyright (c) 2017 2018 Intel Corporation
3*3f7c5e40SJason M. Bills //
4*3f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License");
5*3f7c5e40SJason M. Bills // you may not use this file except in compliance with the License.
6*3f7c5e40SJason M. Bills // You may obtain a copy of the License at
7*3f7c5e40SJason M. Bills //
8*3f7c5e40SJason M. Bills //      http://www.apache.org/licenses/LICENSE-2.0
9*3f7c5e40SJason M. Bills //
10*3f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software
11*3f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS,
12*3f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*3f7c5e40SJason M. Bills // See the License for the specific language governing permissions and
14*3f7c5e40SJason M. Bills // limitations under the License.
15*3f7c5e40SJason M. Bills */
16*3f7c5e40SJason M. Bills 
17*3f7c5e40SJason M. Bills #include <host-ipmid/ipmid-api.h>
18*3f7c5e40SJason M. Bills 
19*3f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp>
20*3f7c5e40SJason M. Bills #include <commandutils.hpp>
21*3f7c5e40SJason M. Bills #include <iostream>
22*3f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp>
23*3f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp>
24*3f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp>
25*3f7c5e40SJason M. Bills #include <storagecommands.hpp>
26*3f7c5e40SJason M. Bills 
27*3f7c5e40SJason M. Bills namespace ipmi
28*3f7c5e40SJason M. Bills {
29*3f7c5e40SJason M. Bills 
30*3f7c5e40SJason M. Bills namespace storage
31*3f7c5e40SJason M. Bills {
32*3f7c5e40SJason M. Bills 
33*3f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16;
34*3f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map<
35*3f7c5e40SJason M. Bills     sdbusplus::message::object_path,
36*3f7c5e40SJason M. Bills     boost::container::flat_map<
37*3f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
38*3f7c5e40SJason M. Bills using ManagedEntry = std::pair<
39*3f7c5e40SJason M. Bills     sdbusplus::message::object_path,
40*3f7c5e40SJason M. Bills     boost::container::flat_map<
41*3f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
42*3f7c5e40SJason M. Bills 
43*3f7c5e40SJason M. Bills constexpr static const char* fruDeviceServiceName = "com.intel.FruDevice";
44*3f7c5e40SJason M. Bills 
45*3f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache;
46*3f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF;
47*3f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF;
48*3f7c5e40SJason M. Bills 
49*3f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
50*3f7c5e40SJason M. Bills 
51*3f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a
52*3f7c5e40SJason M. Bills // collision to verify our dev-id
53*3f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
54*3f7c5e40SJason M. Bills 
55*3f7c5e40SJason M. Bills static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
56*3f7c5e40SJason M. Bills 
57*3f7c5e40SJason M. Bills bool writeFru()
58*3f7c5e40SJason M. Bills {
59*3f7c5e40SJason M. Bills     sdbusplus::message::message writeFru = dbus.new_method_call(
60*3f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
61*3f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "WriteFru");
62*3f7c5e40SJason M. Bills     writeFru.append(cacheBus, cacheAddr, fruCache);
63*3f7c5e40SJason M. Bills     try
64*3f7c5e40SJason M. Bills     {
65*3f7c5e40SJason M. Bills         sdbusplus::message::message writeFruResp = dbus.call(writeFru);
66*3f7c5e40SJason M. Bills     }
67*3f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
68*3f7c5e40SJason M. Bills     {
69*3f7c5e40SJason M. Bills         // todo: log sel?
70*3f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
71*3f7c5e40SJason M. Bills             "error writing fru");
72*3f7c5e40SJason M. Bills         return false;
73*3f7c5e40SJason M. Bills     }
74*3f7c5e40SJason M. Bills     return true;
75*3f7c5e40SJason M. Bills }
76*3f7c5e40SJason M. Bills 
77*3f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId)
78*3f7c5e40SJason M. Bills {
79*3f7c5e40SJason M. Bills     static uint8_t lastDevId = 0xFF;
80*3f7c5e40SJason M. Bills 
81*3f7c5e40SJason M. Bills     bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
82*3f7c5e40SJason M. Bills     if (lastDevId == devId && timerRunning)
83*3f7c5e40SJason M. Bills     {
84*3f7c5e40SJason M. Bills         return IPMI_CC_OK; // cache already up to date
85*3f7c5e40SJason M. Bills     }
86*3f7c5e40SJason M. Bills     // if timer is running, stop it and writeFru manually
87*3f7c5e40SJason M. Bills     else if (timerRunning)
88*3f7c5e40SJason M. Bills     {
89*3f7c5e40SJason M. Bills         cacheTimer->stop();
90*3f7c5e40SJason M. Bills         writeFru();
91*3f7c5e40SJason M. Bills     }
92*3f7c5e40SJason M. Bills 
93*3f7c5e40SJason M. Bills     sdbusplus::message::message getObjects = dbus.new_method_call(
94*3f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
95*3f7c5e40SJason M. Bills         "GetManagedObjects");
96*3f7c5e40SJason M. Bills     ManagedObjectType frus;
97*3f7c5e40SJason M. Bills     try
98*3f7c5e40SJason M. Bills     {
99*3f7c5e40SJason M. Bills         sdbusplus::message::message resp = dbus.call(getObjects);
100*3f7c5e40SJason M. Bills         resp.read(frus);
101*3f7c5e40SJason M. Bills     }
102*3f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
103*3f7c5e40SJason M. Bills     {
104*3f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
105*3f7c5e40SJason M. Bills             "replaceCacheFru: error getting managed objects");
106*3f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
107*3f7c5e40SJason M. Bills     }
108*3f7c5e40SJason M. Bills 
109*3f7c5e40SJason M. Bills     deviceHashes.clear();
110*3f7c5e40SJason M. Bills 
111*3f7c5e40SJason M. Bills     // hash the object paths to create unique device id's. increment on
112*3f7c5e40SJason M. Bills     // collision
113*3f7c5e40SJason M. Bills     std::hash<std::string> hasher;
114*3f7c5e40SJason M. Bills     for (const auto& fru : frus)
115*3f7c5e40SJason M. Bills     {
116*3f7c5e40SJason M. Bills         auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
117*3f7c5e40SJason M. Bills         if (fruIface == fru.second.end())
118*3f7c5e40SJason M. Bills         {
119*3f7c5e40SJason M. Bills             continue;
120*3f7c5e40SJason M. Bills         }
121*3f7c5e40SJason M. Bills 
122*3f7c5e40SJason M. Bills         auto busFind = fruIface->second.find("BUS");
123*3f7c5e40SJason M. Bills         auto addrFind = fruIface->second.find("ADDRESS");
124*3f7c5e40SJason M. Bills         if (busFind == fruIface->second.end() ||
125*3f7c5e40SJason M. Bills             addrFind == fruIface->second.end())
126*3f7c5e40SJason M. Bills         {
127*3f7c5e40SJason M. Bills             phosphor::logging::log<phosphor::logging::level::INFO>(
128*3f7c5e40SJason M. Bills                 "fru device missing Bus or Address",
129*3f7c5e40SJason M. Bills                 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
130*3f7c5e40SJason M. Bills             continue;
131*3f7c5e40SJason M. Bills         }
132*3f7c5e40SJason M. Bills 
133*3f7c5e40SJason M. Bills         uint8_t fruBus =
134*3f7c5e40SJason M. Bills             sdbusplus::message::variant_ns::get<uint32_t>(busFind->second);
135*3f7c5e40SJason M. Bills         uint8_t fruAddr =
136*3f7c5e40SJason M. Bills             sdbusplus::message::variant_ns::get<uint32_t>(addrFind->second);
137*3f7c5e40SJason M. Bills 
138*3f7c5e40SJason M. Bills         uint8_t fruHash = 0;
139*3f7c5e40SJason M. Bills         if (fruBus != 0 || fruAddr != 0)
140*3f7c5e40SJason M. Bills         {
141*3f7c5e40SJason M. Bills             fruHash = hasher(fru.first.str);
142*3f7c5e40SJason M. Bills             // can't be 0xFF based on spec, and 0 is reserved for baseboard
143*3f7c5e40SJason M. Bills             if (fruHash == 0 || fruHash == 0xFF)
144*3f7c5e40SJason M. Bills             {
145*3f7c5e40SJason M. Bills                 fruHash = 1;
146*3f7c5e40SJason M. Bills             }
147*3f7c5e40SJason M. Bills         }
148*3f7c5e40SJason M. Bills         std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr);
149*3f7c5e40SJason M. Bills 
150*3f7c5e40SJason M. Bills         bool emplacePassed = false;
151*3f7c5e40SJason M. Bills         while (!emplacePassed)
152*3f7c5e40SJason M. Bills         {
153*3f7c5e40SJason M. Bills             auto resp = deviceHashes.emplace(fruHash, newDev);
154*3f7c5e40SJason M. Bills             emplacePassed = resp.second;
155*3f7c5e40SJason M. Bills             if (!emplacePassed)
156*3f7c5e40SJason M. Bills             {
157*3f7c5e40SJason M. Bills                 fruHash++;
158*3f7c5e40SJason M. Bills                 // can't be 0xFF based on spec, and 0 is reserved for
159*3f7c5e40SJason M. Bills                 // baseboard
160*3f7c5e40SJason M. Bills                 if (fruHash == 0XFF)
161*3f7c5e40SJason M. Bills                 {
162*3f7c5e40SJason M. Bills                     fruHash = 0x1;
163*3f7c5e40SJason M. Bills                 }
164*3f7c5e40SJason M. Bills             }
165*3f7c5e40SJason M. Bills         }
166*3f7c5e40SJason M. Bills     }
167*3f7c5e40SJason M. Bills     auto deviceFind = deviceHashes.find(devId);
168*3f7c5e40SJason M. Bills     if (deviceFind == deviceHashes.end())
169*3f7c5e40SJason M. Bills     {
170*3f7c5e40SJason M. Bills         return IPMI_CC_SENSOR_INVALID;
171*3f7c5e40SJason M. Bills     }
172*3f7c5e40SJason M. Bills 
173*3f7c5e40SJason M. Bills     fruCache.clear();
174*3f7c5e40SJason M. Bills     sdbusplus::message::message getRawFru = dbus.new_method_call(
175*3f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
176*3f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
177*3f7c5e40SJason M. Bills     cacheBus = deviceFind->second.first;
178*3f7c5e40SJason M. Bills     cacheAddr = deviceFind->second.second;
179*3f7c5e40SJason M. Bills     getRawFru.append(cacheBus, cacheAddr);
180*3f7c5e40SJason M. Bills     try
181*3f7c5e40SJason M. Bills     {
182*3f7c5e40SJason M. Bills         sdbusplus::message::message getRawResp = dbus.call(getRawFru);
183*3f7c5e40SJason M. Bills         getRawResp.read(fruCache);
184*3f7c5e40SJason M. Bills     }
185*3f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
186*3f7c5e40SJason M. Bills     {
187*3f7c5e40SJason M. Bills         lastDevId = 0xFF;
188*3f7c5e40SJason M. Bills         cacheBus = 0xFF;
189*3f7c5e40SJason M. Bills         cacheAddr = 0xFF;
190*3f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
191*3f7c5e40SJason M. Bills     }
192*3f7c5e40SJason M. Bills 
193*3f7c5e40SJason M. Bills     lastDevId = devId;
194*3f7c5e40SJason M. Bills     return IPMI_CC_OK;
195*3f7c5e40SJason M. Bills }
196*3f7c5e40SJason M. Bills 
197*3f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count)
198*3f7c5e40SJason M. Bills {
199*3f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0);
200*3f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
201*3f7c5e40SJason M. Bills     {
202*3f7c5e40SJason M. Bills         return ret;
203*3f7c5e40SJason M. Bills     }
204*3f7c5e40SJason M. Bills     count = deviceHashes.size();
205*3f7c5e40SJason M. Bills     return IPMI_CC_OK;
206*3f7c5e40SJason M. Bills }
207*3f7c5e40SJason M. Bills 
208*3f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
209*3f7c5e40SJason M. Bills {
210*3f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
211*3f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
212*3f7c5e40SJason M. Bills     {
213*3f7c5e40SJason M. Bills         return ret;
214*3f7c5e40SJason M. Bills     }
215*3f7c5e40SJason M. Bills     if (deviceHashes.size() < index)
216*3f7c5e40SJason M. Bills     {
217*3f7c5e40SJason M. Bills         return IPMI_CC_INVALID_FIELD_REQUEST;
218*3f7c5e40SJason M. Bills     }
219*3f7c5e40SJason M. Bills     auto device = deviceHashes.begin() + index;
220*3f7c5e40SJason M. Bills     uint8_t& bus = device->second.first;
221*3f7c5e40SJason M. Bills     uint8_t& address = device->second.second;
222*3f7c5e40SJason M. Bills 
223*3f7c5e40SJason M. Bills     ManagedObjectType frus;
224*3f7c5e40SJason M. Bills 
225*3f7c5e40SJason M. Bills     sdbusplus::message::message getObjects = dbus.new_method_call(
226*3f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
227*3f7c5e40SJason M. Bills         "GetManagedObjects");
228*3f7c5e40SJason M. Bills     try
229*3f7c5e40SJason M. Bills     {
230*3f7c5e40SJason M. Bills         sdbusplus::message::message resp = dbus.call(getObjects);
231*3f7c5e40SJason M. Bills         resp.read(frus);
232*3f7c5e40SJason M. Bills     }
233*3f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
234*3f7c5e40SJason M. Bills     {
235*3f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
236*3f7c5e40SJason M. Bills     }
237*3f7c5e40SJason M. Bills     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
238*3f7c5e40SJason M. Bills     auto fru =
239*3f7c5e40SJason M. Bills         std::find_if(frus.begin(), frus.end(),
240*3f7c5e40SJason M. Bills                      [bus, address, &fruData](ManagedEntry& entry) {
241*3f7c5e40SJason M. Bills                          auto findFruDevice =
242*3f7c5e40SJason M. Bills                              entry.second.find("xyz.openbmc_project.FruDevice");
243*3f7c5e40SJason M. Bills                          if (findFruDevice == entry.second.end())
244*3f7c5e40SJason M. Bills                          {
245*3f7c5e40SJason M. Bills                              return false;
246*3f7c5e40SJason M. Bills                          }
247*3f7c5e40SJason M. Bills                          fruData = &(findFruDevice->second);
248*3f7c5e40SJason M. Bills                          auto findBus = findFruDevice->second.find("BUS");
249*3f7c5e40SJason M. Bills                          auto findAddress =
250*3f7c5e40SJason M. Bills                              findFruDevice->second.find("ADDRESS");
251*3f7c5e40SJason M. Bills                          if (findBus == findFruDevice->second.end() ||
252*3f7c5e40SJason M. Bills                              findAddress == findFruDevice->second.end())
253*3f7c5e40SJason M. Bills                          {
254*3f7c5e40SJason M. Bills                              return false;
255*3f7c5e40SJason M. Bills                          }
256*3f7c5e40SJason M. Bills                          if (sdbusplus::message::variant_ns::get<uint32_t>(
257*3f7c5e40SJason M. Bills                                  findBus->second) != bus)
258*3f7c5e40SJason M. Bills                          {
259*3f7c5e40SJason M. Bills                              return false;
260*3f7c5e40SJason M. Bills                          }
261*3f7c5e40SJason M. Bills                          if (sdbusplus::message::variant_ns::get<uint32_t>(
262*3f7c5e40SJason M. Bills                                  findAddress->second) != address)
263*3f7c5e40SJason M. Bills                          {
264*3f7c5e40SJason M. Bills                              return false;
265*3f7c5e40SJason M. Bills                          }
266*3f7c5e40SJason M. Bills                          return true;
267*3f7c5e40SJason M. Bills                      });
268*3f7c5e40SJason M. Bills     if (fru == frus.end())
269*3f7c5e40SJason M. Bills     {
270*3f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
271*3f7c5e40SJason M. Bills     }
272*3f7c5e40SJason M. Bills     std::string name;
273*3f7c5e40SJason M. Bills     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
274*3f7c5e40SJason M. Bills     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
275*3f7c5e40SJason M. Bills     if (findProductName != fruData->end())
276*3f7c5e40SJason M. Bills     {
277*3f7c5e40SJason M. Bills         name = sdbusplus::message::variant_ns::get<std::string>(
278*3f7c5e40SJason M. Bills             findProductName->second);
279*3f7c5e40SJason M. Bills     }
280*3f7c5e40SJason M. Bills     else if (findBoardName != fruData->end())
281*3f7c5e40SJason M. Bills     {
282*3f7c5e40SJason M. Bills         name = sdbusplus::message::variant_ns::get<std::string>(
283*3f7c5e40SJason M. Bills             findBoardName->second);
284*3f7c5e40SJason M. Bills     }
285*3f7c5e40SJason M. Bills     else
286*3f7c5e40SJason M. Bills     {
287*3f7c5e40SJason M. Bills         name = "UNKNOWN";
288*3f7c5e40SJason M. Bills     }
289*3f7c5e40SJason M. Bills     if (name.size() > maxFruSdrNameSize)
290*3f7c5e40SJason M. Bills     {
291*3f7c5e40SJason M. Bills         name = name.substr(0, maxFruSdrNameSize);
292*3f7c5e40SJason M. Bills     }
293*3f7c5e40SJason M. Bills     size_t sizeDiff = maxFruSdrNameSize - name.size();
294*3f7c5e40SJason M. Bills 
295*3f7c5e40SJason M. Bills     resp.header.record_id_lsb = 0x0; // calling code is to implement these
296*3f7c5e40SJason M. Bills     resp.header.record_id_msb = 0x0;
297*3f7c5e40SJason M. Bills     resp.header.sdr_version = ipmiSdrVersion;
298*3f7c5e40SJason M. Bills     resp.header.record_type = 0x11; // FRU Device Locator
299*3f7c5e40SJason M. Bills     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
300*3f7c5e40SJason M. Bills     resp.key.deviceAddress = 0x20;
301*3f7c5e40SJason M. Bills     resp.key.fruID = device->first;
302*3f7c5e40SJason M. Bills     resp.key.accessLun = 0x80; // logical / physical fru device
303*3f7c5e40SJason M. Bills     resp.key.channelNumber = 0x0;
304*3f7c5e40SJason M. Bills     resp.body.reserved = 0x0;
305*3f7c5e40SJason M. Bills     resp.body.deviceType = 0x10;
306*3f7c5e40SJason M. Bills     resp.body.entityID = 0x0;
307*3f7c5e40SJason M. Bills     resp.body.entityInstance = 0x1;
308*3f7c5e40SJason M. Bills     resp.body.oem = 0x0;
309*3f7c5e40SJason M. Bills     resp.body.deviceIDLen = name.size();
310*3f7c5e40SJason M. Bills     name.copy(resp.body.deviceID, name.size());
311*3f7c5e40SJason M. Bills 
312*3f7c5e40SJason M. Bills     return IPMI_CC_OK;
313*3f7c5e40SJason M. Bills }
314*3f7c5e40SJason M. Bills } // namespace storage
315*3f7c5e40SJason M. Bills } // namespace ipmi