1e7d23d0eSVijay Khemka /*
2e7d23d0eSVijay Khemka * Copyright (c) 2018 Intel Corporation.
3e7d23d0eSVijay Khemka * Copyright (c) 2018-present Facebook.
4e7d23d0eSVijay Khemka *
5e7d23d0eSVijay Khemka * Licensed under the Apache License, Version 2.0 (the "License");
6e7d23d0eSVijay Khemka * you may not use this file except in compliance with the License.
7e7d23d0eSVijay Khemka * You may obtain a copy of the License at
8e7d23d0eSVijay Khemka *
9e7d23d0eSVijay Khemka * http://www.apache.org/licenses/LICENSE-2.0
10e7d23d0eSVijay Khemka *
11e7d23d0eSVijay Khemka * Unless required by applicable law or agreed to in writing, software
12e7d23d0eSVijay Khemka * distributed under the License is distributed on an "AS IS" BASIS,
13e7d23d0eSVijay Khemka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14e7d23d0eSVijay Khemka * See the License for the specific language governing permissions and
15e7d23d0eSVijay Khemka * limitations under the License.
16e7d23d0eSVijay Khemka */
17e7d23d0eSVijay Khemka
18e7d23d0eSVijay Khemka #include <ipmid/api.h>
19e7d23d0eSVijay Khemka
20e7d23d0eSVijay Khemka #include <boost/container/flat_map.hpp>
21e7d23d0eSVijay Khemka #include <commandutils.hpp>
221b6fae3fSVijay Khemka #include <ipmid/utils.hpp>
23e7d23d0eSVijay Khemka #include <phosphor-logging/log.hpp>
24e7d23d0eSVijay Khemka #include <sdbusplus/message/types.hpp>
25e7d23d0eSVijay Khemka #include <sdbusplus/timer.hpp>
26e7d23d0eSVijay Khemka #include <sensorutils.hpp>
27e7d23d0eSVijay Khemka #include <storagecommands.hpp>
28e7d23d0eSVijay Khemka
2963c99be4SVijay Khemka #include <iostream>
302405ae98SPatrick Williams #include <unordered_map>
3163c99be4SVijay Khemka
32e7d23d0eSVijay Khemka namespace ipmi
33e7d23d0eSVijay Khemka {
34e7d23d0eSVijay Khemka
35e7d23d0eSVijay Khemka namespace storage
36e7d23d0eSVijay Khemka {
37e7d23d0eSVijay Khemka void registerStorageFunctions() __attribute__((constructor));
38e7d23d0eSVijay Khemka
39e7d23d0eSVijay Khemka constexpr static const size_t maxMessageSize = 64;
40e7d23d0eSVijay Khemka constexpr static const size_t maxFruSdrNameSize = 16;
41e7d23d0eSVijay Khemka static constexpr int sensorMapUpdatePeriod = 2;
42e7d23d0eSVijay Khemka using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
43e7d23d0eSVijay Khemka
44e7d23d0eSVijay Khemka using ManagedObjectSensor =
45e7d23d0eSVijay Khemka std::map<sdbusplus::message::object_path,
46e7d23d0eSVijay Khemka std::map<std::string, std::map<std::string, DbusVariant>>>;
47e7d23d0eSVijay Khemka
48e7d23d0eSVijay Khemka static uint16_t sdrReservationID;
49e7d23d0eSVijay Khemka
50e7d23d0eSVijay Khemka static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
51e7d23d0eSVijay Khemka static SensorSubTree sensorTree;
52e7d23d0eSVijay Khemka
53e7d23d0eSVijay Khemka void registerSensorFunctions() __attribute__((constructor));
54e7d23d0eSVijay Khemka using ManagedObjectType = boost::container::flat_map<
55e7d23d0eSVijay Khemka sdbusplus::message::object_path,
56e7d23d0eSVijay Khemka boost::container::flat_map<
57e7d23d0eSVijay Khemka std::string, boost::container::flat_map<std::string, DbusVariant>>>;
58e7d23d0eSVijay Khemka using ManagedEntry = std::pair<
59e7d23d0eSVijay Khemka sdbusplus::message::object_path,
60e7d23d0eSVijay Khemka boost::container::flat_map<
61e7d23d0eSVijay Khemka std::string, boost::container::flat_map<std::string, DbusVariant>>>;
62e7d23d0eSVijay Khemka
63e7d23d0eSVijay Khemka constexpr static const char* fruDeviceServiceName =
64e7d23d0eSVijay Khemka "xyz.openbmc_project.FruDevice";
65e7d23d0eSVijay Khemka constexpr static const size_t cacheTimeoutSeconds = 10;
66e7d23d0eSVijay Khemka
67e7d23d0eSVijay Khemka static std::vector<uint8_t> fruCache;
684de58763SPotin Lai static uint16_t cacheBus = 0xFFFF;
69e7d23d0eSVijay Khemka static uint8_t cacheAddr = 0XFF;
70e7d23d0eSVijay Khemka
71d79f89e7SPatrick Williams std::unique_ptr<sdbusplus::Timer> cacheTimer = nullptr;
72e7d23d0eSVijay Khemka
73e7d23d0eSVijay Khemka // we unfortunately have to build a map of hashes in case there is a
74e7d23d0eSVijay Khemka // collision to verify our dev-id
75e7d23d0eSVijay Khemka boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
76e7d23d0eSVijay Khemka
77cd315e07SPatrick Williams static sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
78e7d23d0eSVijay Khemka
792ca4aa0eSDelphine CC Chiu using InterfaceName = std::string;
802ca4aa0eSDelphine CC Chiu using PropertyName = std::string;
812ca4aa0eSDelphine CC Chiu using ThresholdStr = std::string;
822ca4aa0eSDelphine CC Chiu
832ca4aa0eSDelphine CC Chiu enum class AlarmType
842ca4aa0eSDelphine CC Chiu {
852ca4aa0eSDelphine CC Chiu low,
862ca4aa0eSDelphine CC Chiu high
872ca4aa0eSDelphine CC Chiu };
882ca4aa0eSDelphine CC Chiu
892ca4aa0eSDelphine CC Chiu struct Property
902ca4aa0eSDelphine CC Chiu {
912ca4aa0eSDelphine CC Chiu PropertyName name;
922ca4aa0eSDelphine CC Chiu ThresholdStr threshold;
932ca4aa0eSDelphine CC Chiu };
942ca4aa0eSDelphine CC Chiu
952ca4aa0eSDelphine CC Chiu const std::vector<InterfaceName> thresholdCheckedOrder{
962ca4aa0eSDelphine CC Chiu "xyz.openbmc_project.Sensor.Threshold.HardShutdown",
972ca4aa0eSDelphine CC Chiu "xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
982ca4aa0eSDelphine CC Chiu "xyz.openbmc_project.Sensor.Threshold.Critical",
992ca4aa0eSDelphine CC Chiu "xyz.openbmc_project.Sensor.Threshold.Warning"};
1002ca4aa0eSDelphine CC Chiu
1012ca4aa0eSDelphine CC Chiu const std::unordered_map<std::string, std::map<AlarmType, Property>>
1022ca4aa0eSDelphine CC Chiu alarmProperties{
1032ca4aa0eSDelphine CC Chiu {"xyz.openbmc_project.Sensor.Threshold.HardShutdown",
1042ca4aa0eSDelphine CC Chiu {{AlarmType::low, Property{"HardShutdownAlarmLow", "LNR"}},
1052ca4aa0eSDelphine CC Chiu {AlarmType::high, Property{"HardShutdownAlarmHigh", "UNR"}}}},
1062ca4aa0eSDelphine CC Chiu
1072ca4aa0eSDelphine CC Chiu {"xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
1082ca4aa0eSDelphine CC Chiu {{AlarmType::low, Property{"SoftShutdownAlarmLow", "LNR"}},
1092ca4aa0eSDelphine CC Chiu {AlarmType::high, Property{"SoftShutdownAlarmHigh", "UNR"}}}},
1102ca4aa0eSDelphine CC Chiu
1112ca4aa0eSDelphine CC Chiu {"xyz.openbmc_project.Sensor.Threshold.Critical",
1122ca4aa0eSDelphine CC Chiu {{AlarmType::low, Property{"CriticalAlarmLow", "LCR"}},
1132ca4aa0eSDelphine CC Chiu {AlarmType::high, Property{"CriticalAlarmHigh", "UCR"}}}},
1142ca4aa0eSDelphine CC Chiu
1152ca4aa0eSDelphine CC Chiu {"xyz.openbmc_project.Sensor.Threshold.Warning",
1162ca4aa0eSDelphine CC Chiu {{AlarmType::low, Property{"WarningAlarmLow", "LNC"}},
1172ca4aa0eSDelphine CC Chiu {AlarmType::high, Property{"WarningAlarmHigh", "UNC"}}}},
1182ca4aa0eSDelphine CC Chiu };
1192ca4aa0eSDelphine CC Chiu
getSensorMap(std::string sensorConnection,std::string sensorPath,SensorMap & sensorMap)120e7d23d0eSVijay Khemka static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
121e7d23d0eSVijay Khemka SensorMap& sensorMap)
122e7d23d0eSVijay Khemka {
123e7d23d0eSVijay Khemka static boost::container::flat_map<
124e7d23d0eSVijay Khemka std::string, std::chrono::time_point<std::chrono::steady_clock>>
125e7d23d0eSVijay Khemka updateTimeMap;
126e7d23d0eSVijay Khemka
127e7d23d0eSVijay Khemka auto updateFind = updateTimeMap.find(sensorConnection);
128e7d23d0eSVijay Khemka auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
129e7d23d0eSVijay Khemka if (updateFind != updateTimeMap.end())
130e7d23d0eSVijay Khemka {
131e7d23d0eSVijay Khemka lastUpdate = updateFind->second;
132e7d23d0eSVijay Khemka }
133e7d23d0eSVijay Khemka
134e7d23d0eSVijay Khemka auto now = std::chrono::steady_clock::now();
135e7d23d0eSVijay Khemka
136e7d23d0eSVijay Khemka if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
137e7d23d0eSVijay Khemka .count() > sensorMapUpdatePeriod)
138e7d23d0eSVijay Khemka {
139e7d23d0eSVijay Khemka updateTimeMap[sensorConnection] = now;
140e7d23d0eSVijay Khemka
141e7d23d0eSVijay Khemka auto managedObj = dbus.new_method_call(
1428ee95d69SPotin Lai sensorConnection.c_str(), "/xyz/openbmc_project/sensors",
1438ee95d69SPotin Lai "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
144e7d23d0eSVijay Khemka
145e7d23d0eSVijay Khemka ManagedObjectSensor managedObjects;
146e7d23d0eSVijay Khemka try
147e7d23d0eSVijay Khemka {
148e7d23d0eSVijay Khemka auto reply = dbus.call(managedObj);
149e7d23d0eSVijay Khemka reply.read(managedObjects);
150e7d23d0eSVijay Khemka }
15135d12546SPatrick Williams catch (const sdbusplus::exception_t&)
152e7d23d0eSVijay Khemka {
153e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(
154e7d23d0eSVijay Khemka "Error getting managed objects from connection",
155e7d23d0eSVijay Khemka phosphor::logging::entry("CONNECTION=%s",
156e7d23d0eSVijay Khemka sensorConnection.c_str()));
157e7d23d0eSVijay Khemka return false;
158e7d23d0eSVijay Khemka }
159e7d23d0eSVijay Khemka
160e7d23d0eSVijay Khemka SensorCache[sensorConnection] = managedObjects;
161e7d23d0eSVijay Khemka }
162e7d23d0eSVijay Khemka auto connection = SensorCache.find(sensorConnection);
163e7d23d0eSVijay Khemka if (connection == SensorCache.end())
164e7d23d0eSVijay Khemka {
165e7d23d0eSVijay Khemka return false;
166e7d23d0eSVijay Khemka }
167e7d23d0eSVijay Khemka auto path = connection->second.find(sensorPath);
168e7d23d0eSVijay Khemka if (path == connection->second.end())
169e7d23d0eSVijay Khemka {
170e7d23d0eSVijay Khemka return false;
171e7d23d0eSVijay Khemka }
172e7d23d0eSVijay Khemka sensorMap = path->second;
173e7d23d0eSVijay Khemka
174e7d23d0eSVijay Khemka return true;
175e7d23d0eSVijay Khemka }
176e7d23d0eSVijay Khemka
writeFru()177e7d23d0eSVijay Khemka bool writeFru()
178e7d23d0eSVijay Khemka {
179cd315e07SPatrick Williams sdbusplus::message_t writeFru = dbus.new_method_call(
180e7d23d0eSVijay Khemka fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
181e7d23d0eSVijay Khemka "xyz.openbmc_project.FruDeviceManager", "WriteFru");
182e7d23d0eSVijay Khemka writeFru.append(cacheBus, cacheAddr, fruCache);
183e7d23d0eSVijay Khemka try
184e7d23d0eSVijay Khemka {
185cd315e07SPatrick Williams sdbusplus::message_t writeFruResp = dbus.call(writeFru);
186e7d23d0eSVijay Khemka }
18735d12546SPatrick Williams catch (const sdbusplus::exception_t&)
188e7d23d0eSVijay Khemka {
189e7d23d0eSVijay Khemka // todo: log sel?
190e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(
191e7d23d0eSVijay Khemka "error writing fru");
192e7d23d0eSVijay Khemka return false;
193e7d23d0eSVijay Khemka }
194e7d23d0eSVijay Khemka return true;
195e7d23d0eSVijay Khemka }
196e7d23d0eSVijay Khemka
createTimer()197e7d23d0eSVijay Khemka void createTimer()
198e7d23d0eSVijay Khemka {
199e7d23d0eSVijay Khemka if (cacheTimer == nullptr)
200e7d23d0eSVijay Khemka {
201d79f89e7SPatrick Williams cacheTimer = std::make_unique<sdbusplus::Timer>(writeFru);
202e7d23d0eSVijay Khemka }
203e7d23d0eSVijay Khemka }
204e7d23d0eSVijay Khemka
replaceCacheFru(uint8_t devId)205e7d23d0eSVijay Khemka ipmi_ret_t replaceCacheFru(uint8_t devId)
206e7d23d0eSVijay Khemka {
207e7d23d0eSVijay Khemka static uint8_t lastDevId = 0xFF;
208e7d23d0eSVijay Khemka
209e7d23d0eSVijay Khemka bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
210e7d23d0eSVijay Khemka if (lastDevId == devId && timerRunning)
211e7d23d0eSVijay Khemka {
212e7d23d0eSVijay Khemka return IPMI_CC_OK; // cache already up to date
213e7d23d0eSVijay Khemka }
214e7d23d0eSVijay Khemka // if timer is running, stop it and writeFru manually
215e7d23d0eSVijay Khemka else if (timerRunning)
216e7d23d0eSVijay Khemka {
217e7d23d0eSVijay Khemka cacheTimer->stop();
218e7d23d0eSVijay Khemka writeFru();
219e7d23d0eSVijay Khemka }
220e7d23d0eSVijay Khemka
221cd315e07SPatrick Williams sdbusplus::message_t getObjects = dbus.new_method_call(
222e7d23d0eSVijay Khemka fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
223e7d23d0eSVijay Khemka "GetManagedObjects");
224e7d23d0eSVijay Khemka ManagedObjectType frus;
225e7d23d0eSVijay Khemka try
226e7d23d0eSVijay Khemka {
227cd315e07SPatrick Williams sdbusplus::message_t resp = dbus.call(getObjects);
228e7d23d0eSVijay Khemka resp.read(frus);
229e7d23d0eSVijay Khemka }
23035d12546SPatrick Williams catch (const sdbusplus::exception_t&)
231e7d23d0eSVijay Khemka {
232e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(
233e7d23d0eSVijay Khemka "replaceCacheFru: error getting managed objects");
234e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
235e7d23d0eSVijay Khemka }
236e7d23d0eSVijay Khemka
237e7d23d0eSVijay Khemka deviceHashes.clear();
238e7d23d0eSVijay Khemka
239b2ae88b4Scchoux uint8_t fruHash = 0;
240b2ae88b4Scchoux uint8_t mbFruBus = 0, mbFruAddr = 0;
241b2ae88b4Scchoux
242b2ae88b4Scchoux auto device = getMbFruDevice();
243b2ae88b4Scchoux if (device)
244b2ae88b4Scchoux {
245b2ae88b4Scchoux std::tie(mbFruBus, mbFruAddr) = *device;
246b2ae88b4Scchoux deviceHashes.emplace(0, std::make_pair(mbFruBus, mbFruAddr));
247b2ae88b4Scchoux fruHash++;
248b2ae88b4Scchoux }
249b2ae88b4Scchoux
250e7d23d0eSVijay Khemka for (const auto& fru : frus)
251e7d23d0eSVijay Khemka {
252e7d23d0eSVijay Khemka auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
253e7d23d0eSVijay Khemka if (fruIface == fru.second.end())
254e7d23d0eSVijay Khemka {
255e7d23d0eSVijay Khemka continue;
256e7d23d0eSVijay Khemka }
257e7d23d0eSVijay Khemka
258e7d23d0eSVijay Khemka auto busFind = fruIface->second.find("BUS");
259e7d23d0eSVijay Khemka auto addrFind = fruIface->second.find("ADDRESS");
260e7d23d0eSVijay Khemka if (busFind == fruIface->second.end() ||
261e7d23d0eSVijay Khemka addrFind == fruIface->second.end())
262e7d23d0eSVijay Khemka {
263e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::INFO>(
264e7d23d0eSVijay Khemka "fru device missing Bus or Address",
265e7d23d0eSVijay Khemka phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
266e7d23d0eSVijay Khemka continue;
267e7d23d0eSVijay Khemka }
268e7d23d0eSVijay Khemka
269ef0efbc4SPatrick Williams uint8_t fruBus = std::get<uint32_t>(busFind->second);
270ef0efbc4SPatrick Williams uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
271b2ae88b4Scchoux if (fruBus != mbFruBus || fruAddr != mbFruAddr)
272e7d23d0eSVijay Khemka {
273b2ae88b4Scchoux deviceHashes.emplace(fruHash, std::make_pair(fruBus, fruAddr));
274e7d23d0eSVijay Khemka fruHash++;
275e7d23d0eSVijay Khemka }
276e7d23d0eSVijay Khemka }
277e7d23d0eSVijay Khemka auto deviceFind = deviceHashes.find(devId);
278e7d23d0eSVijay Khemka if (deviceFind == deviceHashes.end())
279e7d23d0eSVijay Khemka {
280e7d23d0eSVijay Khemka return IPMI_CC_SENSOR_INVALID;
281e7d23d0eSVijay Khemka }
282e7d23d0eSVijay Khemka
283e7d23d0eSVijay Khemka fruCache.clear();
284cd315e07SPatrick Williams sdbusplus::message_t getRawFru = dbus.new_method_call(
285e7d23d0eSVijay Khemka fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
286e7d23d0eSVijay Khemka "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
287e7d23d0eSVijay Khemka cacheBus = deviceFind->second.first;
288e7d23d0eSVijay Khemka cacheAddr = deviceFind->second.second;
289e7d23d0eSVijay Khemka getRawFru.append(cacheBus, cacheAddr);
290e7d23d0eSVijay Khemka try
291e7d23d0eSVijay Khemka {
292cd315e07SPatrick Williams sdbusplus::message_t getRawResp = dbus.call(getRawFru);
293e7d23d0eSVijay Khemka getRawResp.read(fruCache);
294e7d23d0eSVijay Khemka }
29535d12546SPatrick Williams catch (const sdbusplus::exception_t&)
296e7d23d0eSVijay Khemka {
297e7d23d0eSVijay Khemka lastDevId = 0xFF;
2984de58763SPotin Lai cacheBus = 0xFFFF;
299e7d23d0eSVijay Khemka cacheAddr = 0xFF;
300e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
301e7d23d0eSVijay Khemka }
302e7d23d0eSVijay Khemka
303e7d23d0eSVijay Khemka lastDevId = devId;
304e7d23d0eSVijay Khemka return IPMI_CC_OK;
305e7d23d0eSVijay Khemka }
306e7d23d0eSVijay Khemka
ipmiStorageReadFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)307*010dee04SPatrick Williams ipmi_ret_t ipmiStorageReadFRUData(
308*010dee04SPatrick Williams ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
309e39f9393SWilly Tu ipmi_data_len_t dataLen, ipmi_context_t)
310e7d23d0eSVijay Khemka {
311e7d23d0eSVijay Khemka if (*dataLen != 4)
312e7d23d0eSVijay Khemka {
313e7d23d0eSVijay Khemka *dataLen = 0;
314e7d23d0eSVijay Khemka return IPMI_CC_REQ_DATA_LEN_INVALID;
315e7d23d0eSVijay Khemka }
316e7d23d0eSVijay Khemka *dataLen = 0; // default to 0 in case of an error
317e7d23d0eSVijay Khemka
318e7d23d0eSVijay Khemka auto req = static_cast<GetFRUAreaReq*>(request);
319e7d23d0eSVijay Khemka
320e7d23d0eSVijay Khemka if (req->countToRead > maxMessageSize - 1)
321e7d23d0eSVijay Khemka {
322e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
323e7d23d0eSVijay Khemka }
324e7d23d0eSVijay Khemka ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325e7d23d0eSVijay Khemka
326e7d23d0eSVijay Khemka if (status != IPMI_CC_OK)
327e7d23d0eSVijay Khemka {
328e7d23d0eSVijay Khemka return status;
329e7d23d0eSVijay Khemka }
330e7d23d0eSVijay Khemka
331e7d23d0eSVijay Khemka size_t fromFRUByteLen = 0;
332e7d23d0eSVijay Khemka if (req->countToRead + req->fruInventoryOffset < fruCache.size())
333e7d23d0eSVijay Khemka {
334e7d23d0eSVijay Khemka fromFRUByteLen = req->countToRead;
335e7d23d0eSVijay Khemka }
336e7d23d0eSVijay Khemka else if (fruCache.size() > req->fruInventoryOffset)
337e7d23d0eSVijay Khemka {
338e7d23d0eSVijay Khemka fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
339e7d23d0eSVijay Khemka }
340e7d23d0eSVijay Khemka size_t padByteLen = req->countToRead - fromFRUByteLen;
341e7d23d0eSVijay Khemka uint8_t* respPtr = static_cast<uint8_t*>(response);
342e7d23d0eSVijay Khemka *respPtr = req->countToRead;
343e7d23d0eSVijay Khemka std::copy(fruCache.begin() + req->fruInventoryOffset,
344e7d23d0eSVijay Khemka fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
345e7d23d0eSVijay Khemka ++respPtr);
346e7d23d0eSVijay Khemka // if longer than the fru is requested, fill with 0xFF
347e7d23d0eSVijay Khemka if (padByteLen)
348e7d23d0eSVijay Khemka {
349e7d23d0eSVijay Khemka respPtr += fromFRUByteLen;
350e7d23d0eSVijay Khemka std::fill(respPtr, respPtr + padByteLen, 0xFF);
351e7d23d0eSVijay Khemka }
352e7d23d0eSVijay Khemka *dataLen = fromFRUByteLen + 1;
353e7d23d0eSVijay Khemka
354e7d23d0eSVijay Khemka return IPMI_CC_OK;
355e7d23d0eSVijay Khemka }
356e7d23d0eSVijay Khemka
ipmiStorageWriteFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)357*010dee04SPatrick Williams ipmi_ret_t ipmiStorageWriteFRUData(
358*010dee04SPatrick Williams ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
359e39f9393SWilly Tu ipmi_data_len_t dataLen, ipmi_context_t)
360e7d23d0eSVijay Khemka {
361e7d23d0eSVijay Khemka if (*dataLen < 4 ||
362e7d23d0eSVijay Khemka *dataLen >=
363e7d23d0eSVijay Khemka 0xFF + 3) // count written return is one byte, so limit to one
364e7d23d0eSVijay Khemka // byte of data after the three request data bytes
365e7d23d0eSVijay Khemka {
366e7d23d0eSVijay Khemka *dataLen = 0;
367e7d23d0eSVijay Khemka return IPMI_CC_REQ_DATA_LEN_INVALID;
368e7d23d0eSVijay Khemka }
369e7d23d0eSVijay Khemka
370e7d23d0eSVijay Khemka auto req = static_cast<WriteFRUDataReq*>(request);
371e7d23d0eSVijay Khemka size_t writeLen = *dataLen - 3;
372e7d23d0eSVijay Khemka *dataLen = 0; // default to 0 in case of an error
373e7d23d0eSVijay Khemka
374e7d23d0eSVijay Khemka ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
375e7d23d0eSVijay Khemka if (status != IPMI_CC_OK)
376e7d23d0eSVijay Khemka {
377e7d23d0eSVijay Khemka return status;
378e7d23d0eSVijay Khemka }
379e39f9393SWilly Tu size_t lastWriteAddr = req->fruInventoryOffset + writeLen;
380e7d23d0eSVijay Khemka if (fruCache.size() < lastWriteAddr)
381e7d23d0eSVijay Khemka {
382e7d23d0eSVijay Khemka fruCache.resize(req->fruInventoryOffset + writeLen);
383e7d23d0eSVijay Khemka }
384e7d23d0eSVijay Khemka
385e7d23d0eSVijay Khemka std::copy(req->data, req->data + writeLen,
386e7d23d0eSVijay Khemka fruCache.begin() + req->fruInventoryOffset);
387e7d23d0eSVijay Khemka
388e7d23d0eSVijay Khemka bool atEnd = false;
389e7d23d0eSVijay Khemka
390e7d23d0eSVijay Khemka if (fruCache.size() >= sizeof(FRUHeader))
391e7d23d0eSVijay Khemka {
392e7d23d0eSVijay Khemka FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
393e7d23d0eSVijay Khemka
394e39f9393SWilly Tu size_t lastRecordStart = std::max(
395e7d23d0eSVijay Khemka header->internalOffset,
396e7d23d0eSVijay Khemka std::max(header->chassisOffset,
397e7d23d0eSVijay Khemka std::max(header->boardOffset, header->productOffset)));
398e7d23d0eSVijay Khemka // TODO: Handle Multi-Record FRUs?
399e7d23d0eSVijay Khemka
400e7d23d0eSVijay Khemka lastRecordStart *= 8; // header starts in are multiples of 8 bytes
401e7d23d0eSVijay Khemka
402e7d23d0eSVijay Khemka // get the length of the area in multiples of 8 bytes
403e7d23d0eSVijay Khemka if (lastWriteAddr > (lastRecordStart + 1))
404e7d23d0eSVijay Khemka {
405e7d23d0eSVijay Khemka // second byte in record area is the length
406e7d23d0eSVijay Khemka int areaLength(fruCache[lastRecordStart + 1]);
407e7d23d0eSVijay Khemka areaLength *= 8; // it is in multiples of 8 bytes
408e7d23d0eSVijay Khemka
409e7d23d0eSVijay Khemka if (lastWriteAddr >= (areaLength + lastRecordStart))
410e7d23d0eSVijay Khemka {
411e7d23d0eSVijay Khemka atEnd = true;
412e7d23d0eSVijay Khemka }
413e7d23d0eSVijay Khemka }
414e7d23d0eSVijay Khemka }
415e7d23d0eSVijay Khemka uint8_t* respPtr = static_cast<uint8_t*>(response);
416e7d23d0eSVijay Khemka if (atEnd)
417e7d23d0eSVijay Khemka {
418e7d23d0eSVijay Khemka // cancel timer, we're at the end so might as well send it
419e7d23d0eSVijay Khemka cacheTimer->stop();
420e7d23d0eSVijay Khemka if (!writeFru())
421e7d23d0eSVijay Khemka {
422e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
423e7d23d0eSVijay Khemka }
424e7d23d0eSVijay Khemka *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
425e7d23d0eSVijay Khemka }
426e7d23d0eSVijay Khemka else
427e7d23d0eSVijay Khemka {
428e7d23d0eSVijay Khemka // start a timer, if no further data is sent in cacheTimeoutSeconds
429e7d23d0eSVijay Khemka // seconds, check to see if it is valid
430e7d23d0eSVijay Khemka createTimer();
431e7d23d0eSVijay Khemka cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
432e7d23d0eSVijay Khemka std::chrono::seconds(cacheTimeoutSeconds)));
433e7d23d0eSVijay Khemka *respPtr = 0;
434e7d23d0eSVijay Khemka }
435e7d23d0eSVijay Khemka
436e7d23d0eSVijay Khemka *dataLen = 1;
437e7d23d0eSVijay Khemka
438e7d23d0eSVijay Khemka return IPMI_CC_OK;
439e7d23d0eSVijay Khemka }
440e7d23d0eSVijay Khemka
getFruSdrCount(size_t & count)441e7d23d0eSVijay Khemka ipmi_ret_t getFruSdrCount(size_t& count)
442e7d23d0eSVijay Khemka {
443e7d23d0eSVijay Khemka ipmi_ret_t ret = replaceCacheFru(0);
444e7d23d0eSVijay Khemka if (ret != IPMI_CC_OK)
445e7d23d0eSVijay Khemka {
446e7d23d0eSVijay Khemka return ret;
447e7d23d0eSVijay Khemka }
448e7d23d0eSVijay Khemka count = deviceHashes.size();
449e7d23d0eSVijay Khemka return IPMI_CC_OK;
450e7d23d0eSVijay Khemka }
451e7d23d0eSVijay Khemka
getFruSdrs(size_t index,get_sdr::SensorDataFruRecord & resp)452e7d23d0eSVijay Khemka ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
453e7d23d0eSVijay Khemka {
454e7d23d0eSVijay Khemka ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
455e7d23d0eSVijay Khemka if (ret != IPMI_CC_OK)
456e7d23d0eSVijay Khemka {
457e7d23d0eSVijay Khemka return ret;
458e7d23d0eSVijay Khemka }
459e7d23d0eSVijay Khemka if (deviceHashes.size() < index)
460e7d23d0eSVijay Khemka {
461e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
462e7d23d0eSVijay Khemka }
463e7d23d0eSVijay Khemka auto device = deviceHashes.begin() + index;
464e7d23d0eSVijay Khemka uint8_t& bus = device->second.first;
465e7d23d0eSVijay Khemka uint8_t& address = device->second.second;
466e7d23d0eSVijay Khemka
467e7d23d0eSVijay Khemka ManagedObjectType frus;
468e7d23d0eSVijay Khemka
469cd315e07SPatrick Williams sdbusplus::message_t getObjects = dbus.new_method_call(
470e7d23d0eSVijay Khemka fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
471e7d23d0eSVijay Khemka "GetManagedObjects");
472e7d23d0eSVijay Khemka try
473e7d23d0eSVijay Khemka {
474cd315e07SPatrick Williams sdbusplus::message_t resp = dbus.call(getObjects);
475e7d23d0eSVijay Khemka resp.read(frus);
476e7d23d0eSVijay Khemka }
47735d12546SPatrick Williams catch (const sdbusplus::exception_t&)
478e7d23d0eSVijay Khemka {
479e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
480e7d23d0eSVijay Khemka }
481e7d23d0eSVijay Khemka boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
482*010dee04SPatrick Williams auto fru = std::find_if(
483*010dee04SPatrick Williams frus.begin(), frus.end(),
484e7d23d0eSVijay Khemka [bus, address, &fruData](ManagedEntry& entry) {
485*010dee04SPatrick Williams auto findFruDevice =
486*010dee04SPatrick Williams entry.second.find("xyz.openbmc_project.FruDevice");
487e7d23d0eSVijay Khemka if (findFruDevice == entry.second.end())
488e7d23d0eSVijay Khemka {
489e7d23d0eSVijay Khemka return false;
490e7d23d0eSVijay Khemka }
491e7d23d0eSVijay Khemka fruData = &(findFruDevice->second);
492e7d23d0eSVijay Khemka auto findBus = findFruDevice->second.find("BUS");
4932405ae98SPatrick Williams auto findAddress = findFruDevice->second.find("ADDRESS");
494e7d23d0eSVijay Khemka if (findBus == findFruDevice->second.end() ||
495e7d23d0eSVijay Khemka findAddress == findFruDevice->second.end())
496e7d23d0eSVijay Khemka {
497e7d23d0eSVijay Khemka return false;
498e7d23d0eSVijay Khemka }
499ef0efbc4SPatrick Williams if (std::get<uint32_t>(findBus->second) != bus)
500e7d23d0eSVijay Khemka {
501e7d23d0eSVijay Khemka return false;
502e7d23d0eSVijay Khemka }
503ef0efbc4SPatrick Williams if (std::get<uint32_t>(findAddress->second) != address)
504e7d23d0eSVijay Khemka {
505e7d23d0eSVijay Khemka return false;
506e7d23d0eSVijay Khemka }
507e7d23d0eSVijay Khemka return true;
508e7d23d0eSVijay Khemka });
509e7d23d0eSVijay Khemka if (fru == frus.end())
510e7d23d0eSVijay Khemka {
511e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
512e7d23d0eSVijay Khemka }
513e7d23d0eSVijay Khemka std::string name;
514e7d23d0eSVijay Khemka auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
515e7d23d0eSVijay Khemka auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
516e7d23d0eSVijay Khemka if (findProductName != fruData->end())
517e7d23d0eSVijay Khemka {
518ef0efbc4SPatrick Williams name = std::get<std::string>(findProductName->second);
519e7d23d0eSVijay Khemka }
520e7d23d0eSVijay Khemka else if (findBoardName != fruData->end())
521e7d23d0eSVijay Khemka {
522ef0efbc4SPatrick Williams name = std::get<std::string>(findBoardName->second);
523e7d23d0eSVijay Khemka }
524e7d23d0eSVijay Khemka else
525e7d23d0eSVijay Khemka {
526e7d23d0eSVijay Khemka name = "UNKNOWN";
527e7d23d0eSVijay Khemka }
528e7d23d0eSVijay Khemka if (name.size() > maxFruSdrNameSize)
529e7d23d0eSVijay Khemka {
530e7d23d0eSVijay Khemka name = name.substr(0, maxFruSdrNameSize);
531e7d23d0eSVijay Khemka }
532e7d23d0eSVijay Khemka size_t sizeDiff = maxFruSdrNameSize - name.size();
533e7d23d0eSVijay Khemka
534e7d23d0eSVijay Khemka resp.header.record_id_lsb = 0x0; // calling code is to implement these
535e7d23d0eSVijay Khemka resp.header.record_id_msb = 0x0;
536e7d23d0eSVijay Khemka resp.header.sdr_version = ipmiSdrVersion;
537e7d23d0eSVijay Khemka resp.header.record_type = 0x11; // FRU Device Locator
538e7d23d0eSVijay Khemka resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
539e7d23d0eSVijay Khemka resp.key.deviceAddress = 0x20;
540e7d23d0eSVijay Khemka resp.key.fruID = device->first;
541e7d23d0eSVijay Khemka resp.key.accessLun = 0x80; // logical / physical fru device
542e7d23d0eSVijay Khemka resp.key.channelNumber = 0x0;
543e7d23d0eSVijay Khemka resp.body.reserved = 0x0;
544e7d23d0eSVijay Khemka resp.body.deviceType = 0x10;
545e7d23d0eSVijay Khemka resp.body.entityID = 0x0;
546e7d23d0eSVijay Khemka resp.body.entityInstance = 0x1;
547e7d23d0eSVijay Khemka resp.body.oem = 0x0;
548e7d23d0eSVijay Khemka resp.body.deviceIDLen = name.size();
549e7d23d0eSVijay Khemka name.copy(resp.body.deviceID, name.size());
550e7d23d0eSVijay Khemka
551e7d23d0eSVijay Khemka return IPMI_CC_OK;
552e7d23d0eSVijay Khemka }
553e7d23d0eSVijay Khemka
ipmiStorageReserveSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)554e7d23d0eSVijay Khemka ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
555e39f9393SWilly Tu ipmi_request_t, ipmi_response_t response,
556e39f9393SWilly Tu ipmi_data_len_t dataLen, ipmi_context_t)
557e7d23d0eSVijay Khemka {
558e7d23d0eSVijay Khemka printCommand(+netfn, +cmd);
559e7d23d0eSVijay Khemka
560e7d23d0eSVijay Khemka if (*dataLen)
561e7d23d0eSVijay Khemka {
562e7d23d0eSVijay Khemka *dataLen = 0;
563e7d23d0eSVijay Khemka return IPMI_CC_REQ_DATA_LEN_INVALID;
564e7d23d0eSVijay Khemka }
565e7d23d0eSVijay Khemka *dataLen = 0; // default to 0 in case of an error
566e7d23d0eSVijay Khemka sdrReservationID++;
567e7d23d0eSVijay Khemka if (sdrReservationID == 0)
568e7d23d0eSVijay Khemka {
569e7d23d0eSVijay Khemka sdrReservationID++;
570e7d23d0eSVijay Khemka }
571e7d23d0eSVijay Khemka *dataLen = 2;
572e7d23d0eSVijay Khemka auto resp = static_cast<uint8_t*>(response);
573e7d23d0eSVijay Khemka resp[0] = sdrReservationID & 0xFF;
574e7d23d0eSVijay Khemka resp[1] = sdrReservationID >> 8;
575e7d23d0eSVijay Khemka
576e7d23d0eSVijay Khemka return IPMI_CC_OK;
577e7d23d0eSVijay Khemka }
578e7d23d0eSVijay Khemka
ipmiStorageGetSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)579e7d23d0eSVijay Khemka ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
580e7d23d0eSVijay Khemka ipmi_request_t request, ipmi_response_t response,
581e39f9393SWilly Tu ipmi_data_len_t dataLen, ipmi_context_t)
582e7d23d0eSVijay Khemka {
583e7d23d0eSVijay Khemka printCommand(+netfn, +cmd);
584e7d23d0eSVijay Khemka
585e7d23d0eSVijay Khemka if (*dataLen != 6)
586e7d23d0eSVijay Khemka {
587e7d23d0eSVijay Khemka *dataLen = 0;
588e7d23d0eSVijay Khemka return IPMI_CC_REQ_DATA_LEN_INVALID;
589e7d23d0eSVijay Khemka }
590e7d23d0eSVijay Khemka auto requestedSize = *dataLen;
591e7d23d0eSVijay Khemka *dataLen = 0; // default to 0 in case of an error
592e7d23d0eSVijay Khemka
593e7d23d0eSVijay Khemka constexpr uint16_t lastRecordIndex = 0xFFFF;
594e7d23d0eSVijay Khemka auto req = static_cast<GetSDRReq*>(request);
595e7d23d0eSVijay Khemka
596e7d23d0eSVijay Khemka // reservation required for partial reads with non zero offset into
597e7d23d0eSVijay Khemka // record
598e7d23d0eSVijay Khemka if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
599e7d23d0eSVijay Khemka req->offset)
600e7d23d0eSVijay Khemka {
601e7d23d0eSVijay Khemka return IPMI_CC_INVALID_RESERVATION_ID;
602e7d23d0eSVijay Khemka }
603e7d23d0eSVijay Khemka
6045a7a04daSPotin Lai if (!getSensorSubtree(sensorTree) && sensorTree.empty())
605e7d23d0eSVijay Khemka {
606e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
607e7d23d0eSVijay Khemka }
608e7d23d0eSVijay Khemka
609e7d23d0eSVijay Khemka size_t fruCount = 0;
610e7d23d0eSVijay Khemka ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
611e7d23d0eSVijay Khemka if (ret != IPMI_CC_OK)
612e7d23d0eSVijay Khemka {
613e7d23d0eSVijay Khemka return ret;
614e7d23d0eSVijay Khemka }
615e7d23d0eSVijay Khemka
616e7d23d0eSVijay Khemka size_t lastRecord = sensorTree.size() + fruCount - 1;
617e7d23d0eSVijay Khemka if (req->recordID == lastRecordIndex)
618e7d23d0eSVijay Khemka {
619e7d23d0eSVijay Khemka req->recordID = lastRecord;
620e7d23d0eSVijay Khemka }
621e7d23d0eSVijay Khemka if (req->recordID > lastRecord)
622e7d23d0eSVijay Khemka {
623e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
624e7d23d0eSVijay Khemka }
625e7d23d0eSVijay Khemka
6268713427cScchoux uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
627e39f9393SWilly Tu ? req->recordID + 1
628e39f9393SWilly Tu : 0XFFFF;
629e7d23d0eSVijay Khemka
630e7d23d0eSVijay Khemka auto responseClear = static_cast<uint8_t*>(response);
631e7d23d0eSVijay Khemka std::fill(responseClear, responseClear + requestedSize, 0);
632e7d23d0eSVijay Khemka
633e7d23d0eSVijay Khemka auto resp = static_cast<get_sdr::GetSdrResp*>(response);
634e7d23d0eSVijay Khemka resp->next_record_id_lsb = nextRecord & 0xFF;
635e7d23d0eSVijay Khemka resp->next_record_id_msb = nextRecord >> 8;
636e7d23d0eSVijay Khemka
637e7d23d0eSVijay Khemka if (req->recordID >= sensorTree.size())
638e7d23d0eSVijay Khemka {
639e7d23d0eSVijay Khemka size_t fruIndex = req->recordID - sensorTree.size();
640e7d23d0eSVijay Khemka if (fruIndex >= fruCount)
641e7d23d0eSVijay Khemka {
642e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
643e7d23d0eSVijay Khemka }
644e7d23d0eSVijay Khemka get_sdr::SensorDataFruRecord data;
645e7d23d0eSVijay Khemka if (req->offset > sizeof(data))
646e7d23d0eSVijay Khemka {
647e7d23d0eSVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
648e7d23d0eSVijay Khemka }
649e7d23d0eSVijay Khemka ret = ipmi::storage::getFruSdrs(fruIndex, data);
650e7d23d0eSVijay Khemka if (ret != IPMI_CC_OK)
651e7d23d0eSVijay Khemka {
652e7d23d0eSVijay Khemka return ret;
653e7d23d0eSVijay Khemka }
654e7d23d0eSVijay Khemka data.header.record_id_msb = req->recordID << 8;
655e7d23d0eSVijay Khemka data.header.record_id_lsb = req->recordID & 0xFF;
656e7d23d0eSVijay Khemka if (sizeof(data) < (req->offset + req->bytesToRead))
657e7d23d0eSVijay Khemka {
658e7d23d0eSVijay Khemka req->bytesToRead = sizeof(data) - req->offset;
659e7d23d0eSVijay Khemka }
660e7d23d0eSVijay Khemka *dataLen = req->bytesToRead + 2; // next record
661e7d23d0eSVijay Khemka std::memcpy(&resp->record_data, (char*)&data + req->offset,
662e7d23d0eSVijay Khemka req->bytesToRead);
663e7d23d0eSVijay Khemka return IPMI_CC_OK;
664e7d23d0eSVijay Khemka }
665e7d23d0eSVijay Khemka
666e7d23d0eSVijay Khemka std::string connection;
667e7d23d0eSVijay Khemka std::string path;
668e7d23d0eSVijay Khemka uint16_t sensorIndex = req->recordID;
669e7d23d0eSVijay Khemka for (const auto& sensor : sensorTree)
670e7d23d0eSVijay Khemka {
671e7d23d0eSVijay Khemka if (sensorIndex-- == 0)
672e7d23d0eSVijay Khemka {
673e7d23d0eSVijay Khemka if (!sensor.second.size())
674e7d23d0eSVijay Khemka {
675e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
676e7d23d0eSVijay Khemka }
677e7d23d0eSVijay Khemka connection = sensor.second.begin()->first;
678e7d23d0eSVijay Khemka path = sensor.first;
679e7d23d0eSVijay Khemka break;
680e7d23d0eSVijay Khemka }
681e7d23d0eSVijay Khemka }
682e7d23d0eSVijay Khemka
683e7d23d0eSVijay Khemka SensorMap sensorMap;
684e7d23d0eSVijay Khemka if (!getSensorMap(connection, path, sensorMap))
685e7d23d0eSVijay Khemka {
686e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
687e7d23d0eSVijay Khemka }
688e7d23d0eSVijay Khemka uint8_t sensornumber = (req->recordID & 0xFF);
689e39f9393SWilly Tu get_sdr::SensorDataFullRecord record = {};
690e7d23d0eSVijay Khemka
691e7d23d0eSVijay Khemka record.header.record_id_msb = req->recordID << 8;
692e7d23d0eSVijay Khemka record.header.record_id_lsb = req->recordID & 0xFF;
693e7d23d0eSVijay Khemka record.header.sdr_version = ipmiSdrVersion;
694e7d23d0eSVijay Khemka record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
695e7d23d0eSVijay Khemka record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
696e7d23d0eSVijay Khemka sizeof(get_sdr::SensorDataRecordHeader);
697e7d23d0eSVijay Khemka record.key.owner_id = 0x20;
698e7d23d0eSVijay Khemka record.key.owner_lun = 0x0;
699e7d23d0eSVijay Khemka record.key.sensor_number = sensornumber;
700e7d23d0eSVijay Khemka
701e7d23d0eSVijay Khemka record.body.entity_id = 0x0;
702e7d23d0eSVijay Khemka record.body.entity_instance = 0x01;
703e7d23d0eSVijay Khemka record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
704e7d23d0eSVijay Khemka record.body.sensor_type = getSensorTypeFromPath(path);
705e7d23d0eSVijay Khemka std::string type = getSensorTypeStringFromPath(path);
706e7d23d0eSVijay Khemka auto typeCstr = type.c_str();
707e7d23d0eSVijay Khemka auto findUnits = sensorUnits.find(typeCstr);
708e7d23d0eSVijay Khemka if (findUnits != sensorUnits.end())
709e7d23d0eSVijay Khemka {
710e7d23d0eSVijay Khemka record.body.sensor_units_2_base =
711e7d23d0eSVijay Khemka static_cast<uint8_t>(findUnits->second);
712e7d23d0eSVijay Khemka } // else default 0x0 unspecified
713e7d23d0eSVijay Khemka
714e7d23d0eSVijay Khemka record.body.event_reading_type = getSensorEventTypeFromPath(path);
715e7d23d0eSVijay Khemka
716e7d23d0eSVijay Khemka auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
717e7d23d0eSVijay Khemka if (sensorObject == sensorMap.end())
718e7d23d0eSVijay Khemka {
719e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
720e7d23d0eSVijay Khemka }
721e7d23d0eSVijay Khemka
722e7d23d0eSVijay Khemka auto maxObject = sensorObject->second.find("MaxValue");
723e7d23d0eSVijay Khemka auto minObject = sensorObject->second.find("MinValue");
724e7d23d0eSVijay Khemka double max = 128;
725e7d23d0eSVijay Khemka double min = -127;
726e7d23d0eSVijay Khemka if (maxObject != sensorObject->second.end())
727e7d23d0eSVijay Khemka {
728ef0efbc4SPatrick Williams max = std::visit(VariantToDoubleVisitor(), maxObject->second);
729e7d23d0eSVijay Khemka }
730e7d23d0eSVijay Khemka
731e7d23d0eSVijay Khemka if (minObject != sensorObject->second.end())
732e7d23d0eSVijay Khemka {
733ef0efbc4SPatrick Williams min = std::visit(VariantToDoubleVisitor(), minObject->second);
734e7d23d0eSVijay Khemka }
735e7d23d0eSVijay Khemka
736e7d23d0eSVijay Khemka int16_t mValue;
737e7d23d0eSVijay Khemka int8_t rExp;
738e7d23d0eSVijay Khemka int16_t bValue;
739e7d23d0eSVijay Khemka int8_t bExp;
740e7d23d0eSVijay Khemka bool bSigned;
741e7d23d0eSVijay Khemka
742e7d23d0eSVijay Khemka if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
743e7d23d0eSVijay Khemka {
744e7d23d0eSVijay Khemka return IPMI_CC_RESPONSE_ERROR;
745e7d23d0eSVijay Khemka }
746e7d23d0eSVijay Khemka
747e7d23d0eSVijay Khemka // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
748e7d23d0eSVijay Khemka record.body.m_lsb = mValue & 0xFF;
749e7d23d0eSVijay Khemka
750e7d23d0eSVijay Khemka // move the smallest bit of the MSB into place (bit 9)
751e7d23d0eSVijay Khemka // the MSbs are bits 7:8 in m_msb_and_tolerance
752e7d23d0eSVijay Khemka uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
753e7d23d0eSVijay Khemka
754e7d23d0eSVijay Khemka // assign the negative
755e7d23d0eSVijay Khemka if (mValue < 0)
756e7d23d0eSVijay Khemka {
757e7d23d0eSVijay Khemka mMsb |= (1 << 7);
758e7d23d0eSVijay Khemka }
759e7d23d0eSVijay Khemka record.body.m_msb_and_tolerance = mMsb;
760e7d23d0eSVijay Khemka
761e7d23d0eSVijay Khemka record.body.b_lsb = bValue & 0xFF;
762e7d23d0eSVijay Khemka
763e7d23d0eSVijay Khemka // move the smallest bit of the MSB into place
764e7d23d0eSVijay Khemka // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
765e7d23d0eSVijay Khemka uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
766e7d23d0eSVijay Khemka
767e7d23d0eSVijay Khemka // assign the negative
768e7d23d0eSVijay Khemka if (bValue < 0)
769e7d23d0eSVijay Khemka {
770e7d23d0eSVijay Khemka bMsb |= (1 << 7);
771e7d23d0eSVijay Khemka }
772e7d23d0eSVijay Khemka record.body.b_msb_and_accuracy_lsb = bMsb;
773e7d23d0eSVijay Khemka
774e7d23d0eSVijay Khemka record.body.r_b_exponents = bExp & 0x7;
775e7d23d0eSVijay Khemka if (bExp < 0)
776e7d23d0eSVijay Khemka {
777e7d23d0eSVijay Khemka record.body.r_b_exponents |= 1 << 3;
778e7d23d0eSVijay Khemka }
779e7d23d0eSVijay Khemka record.body.r_b_exponents = (rExp & 0x7) << 4;
780e7d23d0eSVijay Khemka if (rExp < 0)
781e7d23d0eSVijay Khemka {
782e7d23d0eSVijay Khemka record.body.r_b_exponents |= 1 << 7;
783e7d23d0eSVijay Khemka }
784e7d23d0eSVijay Khemka
785e7d23d0eSVijay Khemka // todo fill out rest of units
786e7d23d0eSVijay Khemka if (bSigned)
787e7d23d0eSVijay Khemka {
788e7d23d0eSVijay Khemka record.body.sensor_units_1 = 1 << 7;
789e7d23d0eSVijay Khemka }
790e7d23d0eSVijay Khemka
791e7d23d0eSVijay Khemka // populate sensor name from path
792e7d23d0eSVijay Khemka std::string name;
793e7d23d0eSVijay Khemka size_t nameStart = path.rfind("/");
794e7d23d0eSVijay Khemka if (nameStart != std::string::npos)
795e7d23d0eSVijay Khemka {
796e7d23d0eSVijay Khemka name = path.substr(nameStart + 1, std::string::npos - nameStart);
797e7d23d0eSVijay Khemka }
798e7d23d0eSVijay Khemka
799e7d23d0eSVijay Khemka std::replace(name.begin(), name.end(), '_', ' ');
800e7d23d0eSVijay Khemka if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
801e7d23d0eSVijay Khemka {
802e7d23d0eSVijay Khemka name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
803e7d23d0eSVijay Khemka }
804e7d23d0eSVijay Khemka record.body.id_string_info = name.size();
805e7d23d0eSVijay Khemka std::strncpy(record.body.id_string, name.c_str(),
806e7d23d0eSVijay Khemka sizeof(record.body.id_string));
807e7d23d0eSVijay Khemka
808e7d23d0eSVijay Khemka if (sizeof(get_sdr::SensorDataFullRecord) <
809e7d23d0eSVijay Khemka (req->offset + req->bytesToRead))
810e7d23d0eSVijay Khemka {
811e7d23d0eSVijay Khemka req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
812e7d23d0eSVijay Khemka }
813e7d23d0eSVijay Khemka
8142405ae98SPatrick Williams *dataLen = 2 +
8152405ae98SPatrick Williams req->bytesToRead; // bytesToRead + MSB and LSB of next record id
816e7d23d0eSVijay Khemka
817e7d23d0eSVijay Khemka std::memcpy(&resp->record_data, (char*)&record + req->offset,
818e7d23d0eSVijay Khemka req->bytesToRead);
819e7d23d0eSVijay Khemka
820e7d23d0eSVijay Khemka return IPMI_CC_OK;
821e7d23d0eSVijay Khemka }
822e7d23d0eSVijay Khemka
getSensorConnectionByName(std::string & name,std::string & connection,std::string & path)823427b2762SVijay Khemka static int getSensorConnectionByName(std::string& name, std::string& connection,
824427b2762SVijay Khemka std::string& path)
825427b2762SVijay Khemka {
8265a7a04daSPotin Lai if (!getSensorSubtree(sensorTree) && sensorTree.empty())
827427b2762SVijay Khemka {
828427b2762SVijay Khemka return -1;
829427b2762SVijay Khemka }
830427b2762SVijay Khemka
831427b2762SVijay Khemka for (const auto& sensor : sensorTree)
832427b2762SVijay Khemka {
833427b2762SVijay Khemka path = sensor.first;
834427b2762SVijay Khemka if (path.find(name) != std::string::npos)
835427b2762SVijay Khemka {
836427b2762SVijay Khemka connection = sensor.second.begin()->first;
837427b2762SVijay Khemka return 0;
838427b2762SVijay Khemka }
839427b2762SVijay Khemka }
840427b2762SVijay Khemka return -1;
841427b2762SVijay Khemka }
842427b2762SVijay Khemka
getSensorThreshold(std::string & name,std::string & thresholdStr)8432ca4aa0eSDelphine CC Chiu int getSensorThreshold(std::string& name, std::string& thresholdStr)
8442ca4aa0eSDelphine CC Chiu {
8452ca4aa0eSDelphine CC Chiu std::string connection;
8462ca4aa0eSDelphine CC Chiu std::string path;
8472ca4aa0eSDelphine CC Chiu int ret = -1;
8482ca4aa0eSDelphine CC Chiu thresholdStr = "";
8492ca4aa0eSDelphine CC Chiu
8502ca4aa0eSDelphine CC Chiu ret = getSensorConnectionByName(name, connection, path);
8512ca4aa0eSDelphine CC Chiu if (ret < 0)
8522ca4aa0eSDelphine CC Chiu {
8532ca4aa0eSDelphine CC Chiu return ret;
8542ca4aa0eSDelphine CC Chiu }
8552ca4aa0eSDelphine CC Chiu
8562ca4aa0eSDelphine CC Chiu SensorMap sensorMap;
8572ca4aa0eSDelphine CC Chiu if (!getSensorMap(connection, path, sensorMap))
8582ca4aa0eSDelphine CC Chiu {
8592ca4aa0eSDelphine CC Chiu return ret;
8602ca4aa0eSDelphine CC Chiu }
8612ca4aa0eSDelphine CC Chiu
8622ca4aa0eSDelphine CC Chiu // Iterate threshold interfaces with priority order
8632ca4aa0eSDelphine CC Chiu for (auto& interface : thresholdCheckedOrder)
8642ca4aa0eSDelphine CC Chiu {
8652ca4aa0eSDelphine CC Chiu auto interfaceProperty = alarmProperties.find(interface);
8662ca4aa0eSDelphine CC Chiu if (interfaceProperty == alarmProperties.end())
8672ca4aa0eSDelphine CC Chiu {
8682ca4aa0eSDelphine CC Chiu continue;
8692ca4aa0eSDelphine CC Chiu }
8702ca4aa0eSDelphine CC Chiu
8712ca4aa0eSDelphine CC Chiu auto propertyValue = interfaceProperty->second;
8722ca4aa0eSDelphine CC Chiu
8732ca4aa0eSDelphine CC Chiu // Checks threshold properties value in sensorMap
8742ca4aa0eSDelphine CC Chiu auto thresholdInterfaceSensorMap = sensorMap.find(interface);
8752ca4aa0eSDelphine CC Chiu
8762ca4aa0eSDelphine CC Chiu // Ignore if interface not set
8772ca4aa0eSDelphine CC Chiu if (thresholdInterfaceSensorMap == sensorMap.end())
8782ca4aa0eSDelphine CC Chiu {
8792ca4aa0eSDelphine CC Chiu continue;
8802ca4aa0eSDelphine CC Chiu }
8812ca4aa0eSDelphine CC Chiu
8822ca4aa0eSDelphine CC Chiu auto& thresholdMap = thresholdInterfaceSensorMap->second;
8832ca4aa0eSDelphine CC Chiu
8842ca4aa0eSDelphine CC Chiu auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
8852ca4aa0eSDelphine CC Chiu auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
8862ca4aa0eSDelphine CC Chiu if (alarmHigh != thresholdMap.end())
8872ca4aa0eSDelphine CC Chiu {
8882ca4aa0eSDelphine CC Chiu if (std::get<bool>(alarmHigh->second))
8892ca4aa0eSDelphine CC Chiu {
8902ca4aa0eSDelphine CC Chiu thresholdStr = propertyAlarmHigh.threshold;
8912ca4aa0eSDelphine CC Chiu break;
8922ca4aa0eSDelphine CC Chiu }
8932ca4aa0eSDelphine CC Chiu }
8942ca4aa0eSDelphine CC Chiu
8952ca4aa0eSDelphine CC Chiu auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
8962ca4aa0eSDelphine CC Chiu auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
8972ca4aa0eSDelphine CC Chiu if (alarmLow != thresholdMap.end())
8982ca4aa0eSDelphine CC Chiu {
8992ca4aa0eSDelphine CC Chiu if (std::get<bool>(alarmLow->second))
9002ca4aa0eSDelphine CC Chiu {
9012ca4aa0eSDelphine CC Chiu thresholdStr = propertyAlarmLow.threshold;
9022ca4aa0eSDelphine CC Chiu break;
9032ca4aa0eSDelphine CC Chiu }
9042ca4aa0eSDelphine CC Chiu }
9052ca4aa0eSDelphine CC Chiu }
9062ca4aa0eSDelphine CC Chiu
9072ca4aa0eSDelphine CC Chiu return 0;
9082ca4aa0eSDelphine CC Chiu }
9092ca4aa0eSDelphine CC Chiu
getSensorValue(std::string & name,double & val)910427b2762SVijay Khemka int getSensorValue(std::string& name, double& val)
911427b2762SVijay Khemka {
912427b2762SVijay Khemka std::string connection;
913427b2762SVijay Khemka std::string path;
914427b2762SVijay Khemka int ret = -1;
915427b2762SVijay Khemka
916427b2762SVijay Khemka ret = getSensorConnectionByName(name, connection, path);
917427b2762SVijay Khemka if (ret < 0)
918427b2762SVijay Khemka {
919427b2762SVijay Khemka return ret;
920427b2762SVijay Khemka }
921427b2762SVijay Khemka
922427b2762SVijay Khemka SensorMap sensorMap;
923427b2762SVijay Khemka if (!getSensorMap(connection, path, sensorMap))
924427b2762SVijay Khemka {
925427b2762SVijay Khemka return ret;
926427b2762SVijay Khemka }
927427b2762SVijay Khemka auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
928427b2762SVijay Khemka
929427b2762SVijay Khemka if (sensorObject == sensorMap.end() ||
930427b2762SVijay Khemka sensorObject->second.find("Value") == sensorObject->second.end())
931427b2762SVijay Khemka {
932427b2762SVijay Khemka return ret;
933427b2762SVijay Khemka }
934427b2762SVijay Khemka auto& valueVariant = sensorObject->second["Value"];
935427b2762SVijay Khemka val = std::visit(VariantToDoubleVisitor(), valueVariant);
936427b2762SVijay Khemka
937427b2762SVijay Khemka return 0;
938427b2762SVijay Khemka }
939427b2762SVijay Khemka
94058bd5d84SVijay Khemka const static boost::container::flat_map<const char*, std::string, CmpStr>
94158bd5d84SVijay Khemka sensorUnitStr{{{"temperature", "C"},
94258bd5d84SVijay Khemka {"voltage", "V"},
94358bd5d84SVijay Khemka {"current", "mA"},
94458bd5d84SVijay Khemka {"fan_tach", "RPM"},
94558bd5d84SVijay Khemka {"fan_pwm", "RPM"},
94658bd5d84SVijay Khemka {"power", "W"}}};
94758bd5d84SVijay Khemka
getSensorUnit(std::string & name,std::string & unit)94858bd5d84SVijay Khemka int getSensorUnit(std::string& name, std::string& unit)
94958bd5d84SVijay Khemka {
95058bd5d84SVijay Khemka std::string connection;
95158bd5d84SVijay Khemka std::string path;
95258bd5d84SVijay Khemka int ret = -1;
95358bd5d84SVijay Khemka
95458bd5d84SVijay Khemka ret = getSensorConnectionByName(name, connection, path);
95558bd5d84SVijay Khemka if (ret < 0)
95658bd5d84SVijay Khemka {
95758bd5d84SVijay Khemka return ret;
95858bd5d84SVijay Khemka }
95958bd5d84SVijay Khemka
96058bd5d84SVijay Khemka std::string sensorTypeStr = getSensorTypeStringFromPath(path);
96158bd5d84SVijay Khemka auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
96258bd5d84SVijay Khemka if (findSensor != sensorUnitStr.end())
96358bd5d84SVijay Khemka {
96458bd5d84SVijay Khemka unit = findSensor->second;
96558bd5d84SVijay Khemka return 0;
96658bd5d84SVijay Khemka }
96758bd5d84SVijay Khemka else
96858bd5d84SVijay Khemka return -1;
96958bd5d84SVijay Khemka }
97058bd5d84SVijay Khemka
ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)971*010dee04SPatrick Williams ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
972*010dee04SPatrick Williams ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
973e39f9393SWilly Tu ipmi_data_len_t dataLen, ipmi_context_t)
97417cf1349SVijay Khemka {
97517cf1349SVijay Khemka if (*dataLen != 1)
97617cf1349SVijay Khemka {
97717cf1349SVijay Khemka *dataLen = 0;
97817cf1349SVijay Khemka return IPMI_CC_REQ_DATA_LEN_INVALID;
97917cf1349SVijay Khemka }
98017cf1349SVijay Khemka *dataLen = 0; // default to 0 in case of an error
98117cf1349SVijay Khemka
98217cf1349SVijay Khemka uint8_t reqDev = *(static_cast<uint8_t*>(request));
98317cf1349SVijay Khemka if (reqDev == 0xFF)
98417cf1349SVijay Khemka {
98517cf1349SVijay Khemka return IPMI_CC_INVALID_FIELD_REQUEST;
98617cf1349SVijay Khemka }
98717cf1349SVijay Khemka ipmi_ret_t status = replaceCacheFru(reqDev);
98817cf1349SVijay Khemka
98917cf1349SVijay Khemka if (status != IPMI_CC_OK)
99017cf1349SVijay Khemka {
99117cf1349SVijay Khemka return status;
99217cf1349SVijay Khemka }
99317cf1349SVijay Khemka
99417cf1349SVijay Khemka GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
99517cf1349SVijay Khemka respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
99617cf1349SVijay Khemka respPtr->inventorySizeMSB = fruCache.size() >> 8;
99717cf1349SVijay Khemka respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
99817cf1349SVijay Khemka
99917cf1349SVijay Khemka *dataLen = sizeof(GetFRUAreaResp);
100017cf1349SVijay Khemka return IPMI_CC_OK;
100117cf1349SVijay Khemka }
100217cf1349SVijay Khemka
registerStorageFunctions()1003e7d23d0eSVijay Khemka void registerStorageFunctions()
1004e7d23d0eSVijay Khemka {
100517cf1349SVijay Khemka // <Get FRU Inventory Area Info>
100617cf1349SVijay Khemka ipmiPrintAndRegister(
100717cf1349SVijay Khemka NETFUN_STORAGE,
100817cf1349SVijay Khemka static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
100917cf1349SVijay Khemka NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
101017cf1349SVijay Khemka
1011e7d23d0eSVijay Khemka // <READ FRU Data>
1012e7d23d0eSVijay Khemka ipmiPrintAndRegister(
1013e7d23d0eSVijay Khemka NETFUN_STORAGE,
1014e7d23d0eSVijay Khemka static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1015e7d23d0eSVijay Khemka ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1016e7d23d0eSVijay Khemka
1017e7d23d0eSVijay Khemka // <WRITE FRU Data>
1018e7d23d0eSVijay Khemka ipmiPrintAndRegister(
1019e7d23d0eSVijay Khemka NETFUN_STORAGE,
1020e7d23d0eSVijay Khemka static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1021e7d23d0eSVijay Khemka NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1022e7d23d0eSVijay Khemka
1023e7d23d0eSVijay Khemka // <Reserve SDR Repo>
1024e7d23d0eSVijay Khemka ipmiPrintAndRegister(
1025e7d23d0eSVijay Khemka NETFUN_STORAGE,
1026e7d23d0eSVijay Khemka static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1027e7d23d0eSVijay Khemka nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1028e7d23d0eSVijay Khemka
1029e7d23d0eSVijay Khemka // <Get Sdr>
1030e7d23d0eSVijay Khemka ipmiPrintAndRegister(
1031e7d23d0eSVijay Khemka NETFUN_STORAGE,
1032e7d23d0eSVijay Khemka static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1033e7d23d0eSVijay Khemka ipmiStorageGetSDR, PRIVILEGE_USER);
1034e7d23d0eSVijay Khemka return;
1035e7d23d0eSVijay Khemka }
1036e7d23d0eSVijay Khemka } // namespace storage
1037e7d23d0eSVijay Khemka } // namespace ipmi
1038