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